+{if $topads neq ''} +
+{/if} +
diff --git a/500.shtml b/500.shtml new file mode 100644 index 0000000..d6db1bc --- /dev/null +++ b/500.shtml @@ -0,0 +1,25 @@ + + + +
+ + +系统崩溃了……
+ +『奇怪的事情发生了……』
+『请等待我们查出问题所在后进行维修。』
+特此通告。 By 冴月麟
\ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d511905 --- /dev/null +++ b/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + +IP | +Motivo | +Boards | +Banido em | +Expira em | +Moderador | + + + {foreach $bans ban} +
---|---|---|---|---|---|
{md5_decrypt($ban.ip, $seed)} | +{$ban.reason} | +{if $ban.boards eq ''}Todas{else}{replace $ban.boards "|" " / "}{/if} | +{date_format $ban.at "%d/%m @ %H:%M"} | +{if $ban.until eq 0}Nunca{else}{date_format $ban.until "%d/%m @ %H:%M"}{/if} | +{$ban.by} | +
{$faq.message}
+{$last_new.0.message}
+{$new.message}
+
+ {t}Delete post{/t}
+ [] {t}Password{/t} + + + {if $board.enablereporting eq 1} + |
+
+ {t}Report post{t} + {t}Reason{/t} + + {/if} + + |
+
+ {if $thispage eq 0} + {t}Previous{/t} + {else} + + {/if} + | ++ [{if $thispage neq 0}{/if}0{if $thispage neq 0}{/if}] + {section name=pages loop=$numpages} + {strip} + [ + {if $.section.pages.iteration neq $thispage} + {/if} + + {$.section.pages.iteration} + + {if $.section.pages.iteration neq $thispage} + + {/if} + ] + {/strip} + {/section} + | ++ {if $thispage eq $numpages} + {t}Next{/t} + {else} + + {/if} + + | +
+ {if $thispage eq 0} + {t}Previous{/t} + {else} + + {/if} + | ++ [{if $thispage neq 0}{/if}0{if $thispage neq 0}{/if}] + {section name=pages loop=$numpages} + {strip} + [ + {if $.section.pages.iteration neq $thispage} + {/if} + + {$.section.pages.iteration} + + {if $.section.pages.iteration neq $thispage} + + {/if} + ] + {/strip} + {/section} + | ++ {if $thispage eq $numpages} + {t}Next{/t} + {else} + + {/if} + + | +
'. _gettext('Sticky') . ' | '. _gettext('Unsticky') . ' |
+ + + + + + + + + | ';
+ $results_boards = $tc_db->GetAll("SELECT HIGH_PRIORITY `name`, `id` FROM `" . KU_DBPREFIX . "boards` ORDER BY `name` ASC");
+ foreach ($results_boards as $line_board) {
+ $output .= '/'. $line_board['name'] . '/'; + $results = $tc_db->GetAll("SELECT HIGH_PRIORITY `id` FROM `" . KU_DBPREFIX . "posts` WHERE `boardid` = " . $line_board['id'] . " AND `IS_DELETED` = '0' AND `parentid` = '0' AND `stickied` = '1'"); + if (count($results) > 0) { + foreach ($results as $line) { + $output .= '#'. $line['id'] . ''; + } + } else { + $output .= 'No stickied threads. '; + } + } + $output .= ' |
'. _gettext('Lock') . ' | '. _gettext('Unlock') . ' |
+ + + + + + + + + | ';
+ $results_boards = $tc_db->GetAll("SELECT HIGH_PRIORITY `id`, `name` FROM `" . KU_DBPREFIX . "boards` ORDER BY `name` ASC");
+ foreach ($results_boards as $line_board) {
+ $output .= '/'. $line_board['name'] . '/'; + $results = $tc_db->GetAll("SELECT HIGH_PRIORITY `id` FROM `" . KU_DBPREFIX . "posts` WHERE `boardid` = " . $line_board['id'] . " AND `IS_DELETED` = '0' AND `parentid` = '0' AND `locked` = '1'"); + if (count($results) > 0) { + foreach ($results as $line) { + $output .= '#'. $line['id'] . ''; + } + } else { + $output .= 'No locked threads. '; + } + } + $output .= ' |
Board | Post | File | Message | Reason | Reporter IP | Action |
---|---|---|---|---|---|---|
/'. $linereport['board'] . '/ | '. $line['id'] . ' | '; + if ($line['file'] == 'removed') { + $tpl_page .= 'removed'; + } elseif ($line['file'] == '') { + $tpl_page .= 'none'; + } elseif ($line['file_type'] == 'jpg' || $line['file_type'] == 'gif' || $line['file_type'] == 'png') { + $tpl_page .= ''; + } else { + $tpl_page .= 'File'; + } + $tpl_page .= ' | '; + if ($line['message'] != '') { + $tpl_page .= stripslashes($line['message']); + } else { + $tpl_page .= ' '; + } + $tpl_page .= ' | '; + if ($linereport['reason'] != '') { + $tpl_page .= htmlspecialchars(stripslashes($linereport['reason'])); + } else { + $tpl_page .= ' '; + } + $tpl_page .= ' | '. md5_decrypt($linereport['ip'], KU_RANDOMSEED) . ' | Clear [D & B] |
'; + $tpl_page .= ($i == 1) ? _gettext('IP Range') : _gettext('IP Address'); + $tpl_page .= ' | '. _gettext('Boards') . ' | '. _gettext('Reason') . ' | '. _gettext('Staff Note') . ' | '. _gettext('Date added') . ' | '. _gettext('Expires/Expired') . ' | '. _gettext('Added By') . ' | |
---|---|---|---|---|---|---|---|
'. md5_decrypt($line['ip'], KU_RANDOMSEED) . ' | '; + if ($line['globalban'] == 1) { + $tpl_page .= ''. _gettext('All boards') . ''; + } elseif (!empty($line['boards'])) { + $tpl_page .= '/'. implode('/, /', explode('|', $line['boards'])) . '/ '; + } + $tpl_page .= ' | '; + $tpl_page .= (!empty($line['reason'])) ? htmlentities(stripslashes($line['reason'])) : ' '; + $tpl_page .= ' | '; + $tpl_page .= (!empty($line['staffnote'])) ? htmlentities(stripslashes($line['staffnote'])) : ' '; + $tpl_page .= ' | '. date("F j, Y, g:i a", $line['at']) . ' | '; + $tpl_page .= ($line['until'] == 0) ? ''. _gettext('Does not expire') . '' : date("F j, Y, g:i a", $line['until']); + $tpl_page .= ' | '. $line['by'] . ' | ['. _gettext('Delete') .'] |
'. _gettext('Hash') . ' | '. _gettext('Description') . ' | '. _gettext('Ban time') . ' | |
---|---|---|---|
'. _gettext('None') . ' | |||
'. $line['md5'] . ' | '. $line['description'] . ' | '; + $tpl_page .= ($line['bantime'] == 0) ? ''. _gettext('Does not expire') . '' : $line['bantime'] . ' seconds'; + $tpl_page .= ' | [x] |
'; + if ($i == 1) { + $tpl_page .= 'IP Range'; + } else { + $tpl_page .= 'IP Address'; + } + $tpl_page .= ' | Boards | Reason | Staff Note | Date Added | Expires | Added By | Appeal Message | Deny | Accept |
---|---|---|---|---|---|---|---|---|---|
'. md5_decrypt($line['ip'], KU_RANDOMSEED) . ' | '; + if ($line['globalban'] == '1') { + $tpl_page .= ''. _gettext('All boards') . ''; + } else { + if ($line['boards'] != '') { + $tpl_page .= '/'. implode('/, /', explode('|', $line['boards'])) . '/ '; + } + } + $tpl_page .= ' | '; + if ($line['reason'] != '') { + $tpl_page .= htmlentities(stripslashes($line['reason'])); + } else { + $tpl_page .= ' '; + } + $tpl_page .= ' | '; + if ($line['staffnote'] != '') { + $tpl_page .= htmlentities(stripslashes($line['staffnote'])); + } else { + $tpl_page .= ' '; + } + $tpl_page .= ' | '. date("F j, Y, g:i a", $line['at']) . ' | '; + if ($line['until'] == '0') { + $tpl_page .= ''. _gettext('Does not expire') . ''; + } else { + $tpl_page .= date("F j, Y, g:i a", $line['until']); + } + $tpl_page .= ' | '. $line['by'] . ' | +'.$line['appeal'].' | +:( | +:) | '; + $tpl_page .= '
/'. $line['boardname'] . '/'. $line['id'] . ' |
'._gettext('Post Number').' | '._gettext('Post Message').' | '._gettext('Poster IP').' |
---|---|---|
/'. $line['boardname'] . '/'. $line['id'] . ' | '. stripslashes($line['message']) . ' | '. md5_decrypt($line['ip'], KU_RANDOMSEED) . ' |
'; + } + foreach ($results as $line) { + if ($all && $line['important'] == 1) { + $output .= ''; + } elseif (!$all) { + $output .= '' . "\n"; + } elseif (!$all) { + $output .= "\n"; + if ($line['important'] == 1) { + $output .= '
+ + | +|||||
+ + | +
+
+
+
|
+ + + | +|||
+ + | +
+ getdate() with adodb_getdate() + date() with adodb_date() + gmdate() with adodb_gmdate() + mktime() with adodb_mktime() + gmmktime() with adodb_gmmktime() + strftime() with adodb_strftime() + strftime() with adodb_gmstrftime() ++ +The parameters are identical, except that adodb_date() accepts a subset +of date()'s field formats. Mktime() will convert from local time to GMT, +and date() will convert from GMT to local time, but daylight savings is +not handled currently. + +This library is independant of the rest of ADOdb, and can be used +as standalone code. + +PERFORMANCE + +For high speed, this library uses the native date functions where +possible, and only switches to PHP code when the dates fall outside +the 32-bit signed integer range. + +GREGORIAN CORRECTION + +Pope Gregory shortened October of A.D. 1582 by ten days. Thursday, +October 4, 1582 (Julian) was followed immediately by Friday, October 15, +1582 (Gregorian). + +Since 0.06, we handle this correctly, so: + +adodb_mktime(0,0,0,10,15,1582) - adodb_mktime(0,0,0,10,4,1582) + == 24 * 3600 (1 day) + +============================================================================= + +COPYRIGHT + +(c) 2003-2005 John Lim and released under BSD-style license except for code by +jackbbs, which includes adodb_mktime, adodb_get_gmt_diff, adodb_is_leap_year +and originally found at http://www.php.net/manual/en/function.mktime.php + +============================================================================= + +BUG REPORTS + +These should be posted to the ADOdb forums at + + http://phplens.com/lens/lensforum/topics.php?id=4 + +============================================================================= + +FUNCTION DESCRIPTIONS + + +** FUNCTION adodb_getdate($date=false) + +Returns an array containing date information, as getdate(), but supports +dates greater than 1901 to 2038. The local date/time format is derived from a +heuristic the first time adodb_getdate is called. + + +** FUNCTION adodb_date($fmt, $timestamp = false) + +Convert a timestamp to a formatted local date. If $timestamp is not defined, the +current timestamp is used. Unlike the function date(), it supports dates +outside the 1901 to 2038 range. + +The format fields that adodb_date supports: + +
+ a - "am" or "pm" + A - "AM" or "PM" + d - day of the month, 2 digits with leading zeros; i.e. "01" to "31" + D - day of the week, textual, 3 letters; e.g. "Fri" + F - month, textual, long; e.g. "January" + g - hour, 12-hour format without leading zeros; i.e. "1" to "12" + G - hour, 24-hour format without leading zeros; i.e. "0" to "23" + h - hour, 12-hour format; i.e. "01" to "12" + H - hour, 24-hour format; i.e. "00" to "23" + i - minutes; i.e. "00" to "59" + j - day of the month without leading zeros; i.e. "1" to "31" + l (lowercase 'L') - day of the week, textual, long; e.g. "Friday" + L - boolean for whether it is a leap year; i.e. "0" or "1" + m - month; i.e. "01" to "12" + M - month, textual, 3 letters; e.g. "Jan" + n - month without leading zeros; i.e. "1" to "12" + O - Difference to Greenwich time in hours; e.g. "+0200" + Q - Quarter, as in 1, 2, 3, 4 + r - RFC 822 formatted date; e.g. "Thu, 21 Dec 2000 16:01:07 +0200" + s - seconds; i.e. "00" to "59" + S - English ordinal suffix for the day of the month, 2 characters; + i.e. "st", "nd", "rd" or "th" + t - number of days in the given month; i.e. "28" to "31" + T - Timezone setting of this machine; e.g. "EST" or "MDT" + U - seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) + w - day of the week, numeric, i.e. "0" (Sunday) to "6" (Saturday) + Y - year, 4 digits; e.g. "1999" + y - year, 2 digits; e.g. "99" + z - day of the year; i.e. "0" to "365" + Z - timezone offset in seconds (i.e. "-43200" to "43200"). + The offset for timezones west of UTC is always negative, + and for those east of UTC is always positive. ++ +Unsupported: +
+ B - Swatch Internet time + I (capital i) - "1" if Daylight Savings Time, "0" otherwise. + W - ISO-8601 week number of year, weeks starting on Monday + ++ + +** FUNCTION adodb_date2($fmt, $isoDateString = false) +Same as adodb_date, but 2nd parameter accepts iso date, eg. + + adodb_date2('d-M-Y H:i','2003-12-25 13:01:34'); + + +** FUNCTION adodb_gmdate($fmt, $timestamp = false) + +Convert a timestamp to a formatted GMT date. If $timestamp is not defined, the +current timestamp is used. Unlike the function date(), it supports dates +outside the 1901 to 2038 range. + + +** FUNCTION adodb_mktime($hr, $min, $sec[, $month, $day, $year]) + +Converts a local date to a unix timestamp. Unlike the function mktime(), it supports +dates outside the 1901 to 2038 range. All parameters are optional. + + +** FUNCTION adodb_gmmktime($hr, $min, $sec [, $month, $day, $year]) + +Converts a gmt date to a unix timestamp. Unlike the function gmmktime(), it supports +dates outside the 1901 to 2038 range. Differs from gmmktime() in that all parameters +are currently compulsory. + +** FUNCTION adodb_gmstrftime($fmt, $timestamp = false) +Convert a timestamp to a formatted GMT date. + +** FUNCTION adodb_strftime($fmt, $timestamp = false) + +Convert a timestamp to a formatted local date. Internally converts $fmt into +adodb_date format, then echo result. + +For best results, you can define the local date format yourself. Define a global +variable $ADODB_DATE_LOCALE which is an array, 1st element is date format using +adodb_date syntax, and 2nd element is the time format, also in adodb_date syntax. + + eg. $ADODB_DATE_LOCALE = array('d/m/Y','H:i:s'); + + Supported format codes: + +
+ %a - abbreviated weekday name according to the current locale + %A - full weekday name according to the current locale + %b - abbreviated month name according to the current locale + %B - full month name according to the current locale + %c - preferred date and time representation for the current locale + %d - day of the month as a decimal number (range 01 to 31) + %D - same as %m/%d/%y + %e - day of the month as a decimal number, a single digit is preceded by a space (range ' 1' to '31') + %h - same as %b + %H - hour as a decimal number using a 24-hour clock (range 00 to 23) + %I - hour as a decimal number using a 12-hour clock (range 01 to 12) + %m - month as a decimal number (range 01 to 12) + %M - minute as a decimal number + %n - newline character + %p - either `am' or `pm' according to the given time value, or the corresponding strings for the current locale + %r - time in a.m. and p.m. notation + %R - time in 24 hour notation + %S - second as a decimal number + %t - tab character + %T - current time, equal to %H:%M:%S + %x - preferred date representation for the current locale without the time + %X - preferred time representation for the current locale without the date + %y - year as a decimal number without a century (range 00 to 99) + %Y - year as a decimal number including the century + %Z - time zone or name or abbreviation + %% - a literal `%' character ++ + Unsupported codes: +
+ %C - century number (the year divided by 100 and truncated to an integer, range 00 to 99) + %g - like %G, but without the century. + %G - The 4-digit year corresponding to the ISO week number (see %V). + This has the same format and value as %Y, except that if the ISO week number belongs + to the previous or next year, that year is used instead. + %j - day of the year as a decimal number (range 001 to 366) + %u - weekday as a decimal number [1,7], with 1 representing Monday + %U - week number of the current year as a decimal number, starting + with the first Sunday as the first day of the first week + %V - The ISO 8601:1988 week number of the current year as a decimal number, + range 01 to 53, where week 1 is the first week that has at least 4 days in the + current year, and with Monday as the first day of the week. (Use %G or %g for + the year component that corresponds to the week number for the specified timestamp.) + %w - day of the week as a decimal, Sunday being 0 + %W - week number of the current year as a decimal number, starting with the + first Monday as the first day of the first week ++ +============================================================================= + +NOTES + +Useful url for generating test timestamps: + http://www.4webhelp.net/us/timestamp.php + +Possible future optimizations include + +a. Using an algorithm similar to Plauger's in "The Standard C Library" +(page 428, xttotm.c _Ttotm() function). Plauger's algorithm will not +work outside 32-bit signed range, so i decided not to implement it. + +b. Implement daylight savings, which looks awfully complicated, see + http://webexhibits.org/daylightsaving/ + + +CHANGELOG +- 08 Sept 2005 0.22 +In adodb_date2(), $is_gmt not supported properly. Fixed. + +- 18 July 2005 0.21 +In PHP 4.3.11, the 'r' format has changed. Leading 0 in day is added. Changed for compat. +Added support for negative months in adodb_mktime(). + +- 24 Feb 2005 0.20 +Added limited strftime/gmstrftime support. x10 improvement in performance of adodb_date(). + +- 21 Dec 2004 0.17 +In adodb_getdate(), the timestamp was accidentally converted to gmt when $is_gmt is false. +Also adodb_mktime(0,0,0) did not work properly. Both fixed thx Mauro. + +- 17 Nov 2004 0.16 +Removed intval typecast in adodb_mktime() for secs, allowing: + adodb_mktime(0,0,0 + 2236672153,1,1,1934); +Suggested by Ryan. + +- 18 July 2004 0.15 +All params in adodb_mktime were formerly compulsory. Now only the hour, min, secs is compulsory. +This brings it more in line with mktime (still not identical). + +- 23 June 2004 0.14 + +Allow you to define your own daylights savings function, adodb_daylight_sv. +If the function is defined (somewhere in an include), then you can correct for daylights savings. + +In this example, we apply daylights savings in June or July, adding one hour. This is extremely +unrealistic as it does not take into account time-zone, geographic location, current year. + +function adodb_daylight_sv(&$arr, $is_gmt) +{ + if ($is_gmt) return; + $m = $arr['mon']; + if ($m == 6 || $m == 7) $arr['hours'] += 1; +} + +This is only called by adodb_date() and not by adodb_mktime(). + +The format of $arr is +Array ( + [seconds] => 0 + [minutes] => 0 + [hours] => 0 + [mday] => 1 # day of month, eg 1st day of the month + [mon] => 2 # month (eg. Feb) + [year] => 2102 + [yday] => 31 # days in current year + [leap] => # true if leap year + [ndays] => 28 # no of days in current month + ) + + +- 28 Apr 2004 0.13 +Fixed adodb_date to properly support $is_gmt. Thx to Dimitar Angelov. + +- 20 Mar 2004 0.12 +Fixed month calculation error in adodb_date. 2102-June-01 appeared as 2102-May-32. + +- 26 Oct 2003 0.11 +Because of daylight savings problems (some systems apply daylight savings to +January!!!), changed adodb_get_gmt_diff() to ignore daylight savings. + +- 9 Aug 2003 0.10 +Fixed bug with dates after 2038. +See http://phplens.com/lens/lensforum/msgs.php?id=6980 + +- 1 July 2003 0.09 +Added support for Q (Quarter). +Added adodb_date2(), which accepts ISO date in 2nd param + +- 3 March 2003 0.08 +Added support for 'S' adodb_date() format char. Added constant ADODB_ALLOW_NEGATIVE_TS +if you want PHP to handle negative timestamps between 1901 to 1969. + +- 27 Feb 2003 0.07 +All negative numbers handled by adodb now because of RH 7.3+ problems. +See http://bugs.php.net/bug.php?id=20048&edit=2 + +- 4 Feb 2003 0.06 +Fixed a typo, 1852 changed to 1582! This means that pre-1852 dates +are now correctly handled. + +- 29 Jan 2003 0.05 + +Leap year checking differs under Julian calendar (pre 1582). Also +leap year code optimized by checking for most common case first. + +We also handle month overflow correctly in mktime (eg month set to 13). + +Day overflow for less than one month's days is supported. + +- 28 Jan 2003 0.04 + +Gregorian correction handled. In PHP5, we might throw an error if +mktime uses invalid dates around 5-14 Oct 1582. Released with ADOdb 3.10. +Added limbo 5-14 Oct 1582 check, when we set to 15 Oct 1582. + +- 27 Jan 2003 0.03 + +Fixed some more month problems due to gmt issues. Added constant ADODB_DATE_VERSION. +Fixed calculation of days since start of year for <1970. + +- 27 Jan 2003 0.02 + +Changed _adodb_getdate() to inline leap year checking for better performance. +Fixed problem with time-zones west of GMT +0000. + +- 24 Jan 2003 0.01 + +First implementation. +*/ + + +/* Initialization */ + +/* + Version Number +*/ +define('ADODB_DATE_VERSION',0.22); + +/* + This code was originally for windows. But apparently this problem happens + also with Linux, RH 7.3 and later! + + glibc-2.2.5-34 and greater has been changed to return -1 for dates < + 1970. This used to work. The problem exists with RedHat 7.3 and 8.0 + echo (mktime(0, 0, 0, 1, 1, 1960)); // prints -1 + + References: + http://bugs.php.net/bug.php?id=20048&edit=2 + http://lists.debian.org/debian-glibc/2002/debian-glibc-200205/msg00010.html +*/ + +if (!defined('ADODB_ALLOW_NEGATIVE_TS')) define('ADODB_NO_NEGATIVE_TS',1); + +function adodb_date_test_date($y1,$m,$d=13) +{ + $t = adodb_mktime(0,0,0,$m,$d,$y1); + $rez = adodb_date('Y-n-j H:i:s',$t); + if ("$y1-$m-$d 00:00:00" != $rez) { + print "$y1 error, expected=$y1-$m-$d 00:00:00, adodb=$rez
Testing gregorian <=> julian conversion
";
+ $t = adodb_mktime(0,0,0,10,11,1492);
+ //http://www.holidayorigins.com/html/columbus_day.html - Friday check
+ if (!(adodb_date('D Y-m-d',$t) == 'Fri 1492-10-11')) print 'Error in Columbus landing
';
+
+ $t = adodb_mktime(0,0,0,2,29,1500);
+ if (!(adodb_date('Y-m-d',$t) == '1500-02-29')) print 'Error in julian leap years
';
+
+ $t = adodb_mktime(0,0,0,2,29,1700);
+ if (!(adodb_date('Y-m-d',$t) == '1700-03-01')) print 'Error in gregorian leap years
';
+
+ print adodb_mktime(0,0,0,10,4,1582).' ';
+ print adodb_mktime(0,0,0,10,15,1582);
+ $diff = (adodb_mktime(0,0,0,10,15,1582) - adodb_mktime(0,0,0,10,4,1582));
+ if ($diff != 3600*24) print " Error in gregorian correction = ".($diff/3600/24)." days
";
+
+ print " 15 Oct 1582, Fri=".(adodb_dow(1582,10,15) == 5 ? 'Fri' : 'Error')."
";
+ print " 4 Oct 1582, Thu=".(adodb_dow(1582,10,4) == 4 ? 'Thu' : 'Error')."
";
+
+ print "
Testing overflow
";
+
+ $t = adodb_mktime(0,0,0,3,33,1965);
+ if (!(adodb_date('Y-m-d',$t) == '1965-04-02')) print 'Error in day overflow 1
';
+ $t = adodb_mktime(0,0,0,4,33,1971);
+ if (!(adodb_date('Y-m-d',$t) == '1971-05-03')) print 'Error in day overflow 2
';
+ $t = adodb_mktime(0,0,0,1,60,1965);
+ if (!(adodb_date('Y-m-d',$t) == '1965-03-01')) print 'Error in day overflow 3 '.adodb_date('Y-m-d',$t).'
';
+ $t = adodb_mktime(0,0,0,12,32,1965);
+ if (!(adodb_date('Y-m-d',$t) == '1966-01-01')) print 'Error in day overflow 4 '.adodb_date('Y-m-d',$t).'
';
+ $t = adodb_mktime(0,0,0,12,63,1965);
+ if (!(adodb_date('Y-m-d',$t) == '1966-02-01')) print 'Error in day overflow 5 '.adodb_date('Y-m-d',$t).'
';
+ $t = adodb_mktime(0,0,0,13,3,1965);
+ if (!(adodb_date('Y-m-d',$t) == '1966-01-03')) print 'Error in mth overflow 1
';
+
+ print "Testing 2-digit => 4-digit year conversion
";
+ if (adodb_year_digit_check(00) != 2000) print "Err 2-digit 2000
";
+ if (adodb_year_digit_check(10) != 2010) print "Err 2-digit 2010
";
+ if (adodb_year_digit_check(20) != 2020) print "Err 2-digit 2020
";
+ if (adodb_year_digit_check(30) != 2030) print "Err 2-digit 2030
";
+ if (adodb_year_digit_check(40) != 1940) print "Err 2-digit 1940
";
+ if (adodb_year_digit_check(50) != 1950) print "Err 2-digit 1950
";
+ if (adodb_year_digit_check(90) != 1990) print "Err 2-digit 1990
";
+
+ // Test string formating
+ print "
Testing date formating
"; + $fmt = '\d\a\t\e T Y-m-d H:i:s a A d D F g G h H i j l L m M n O \R\F\C822 r s t U w y Y z Z 2003'; + $s1 = date($fmt,0); + $s2 = adodb_date($fmt,0); + if ($s1 != $s2) { + print " date() 0 failed";
+ $pos = strcmp($s1,$s2);
+
+ if (($s1) != ($s2)) {
+ for ($j=0,$k=strlen($s1); $j < $k; $j++) {
+ if ($s1[$j] != $s2[$j]) {
+ print substr($s1,$j).' ';
+ break;
+ }
+ }
+ print "Error date(): $ts
+ \"$s1\" (date len=".strlen($s1).")
+ \"$s2\" (adodb_date len=".strlen($s2).")
";
+ $fail = true;
+ }
+
+ $a1 = getdate($ts);
+ $a2 = adodb_getdate($ts);
+ $rez = array_diff($a1,$a2);
+ if (sizeof($rez)>0) {
+ print "Error getdate() $ts
";
+ print_r($a1);
+ print "
";
+ print_r($a2);
+ print "
"; + $fail = true; + } + } + + // Test generation of dates outside 1901-2038 + print "
Testing random dates between 100 and 4000
"; + adodb_date_test_date(100,1); + for ($i=100; --$i >= 0;) { + $y1 = 100+rand(0,1970-100); + $m = rand(1,12); + adodb_date_test_date($y1,$m); + + $y1 = 3000-rand(0,3000-1970); + adodb_date_test_date($y1,$m); + } + print '';
+ $start = 1960+rand(0,10);
+ $yrs = 12;
+ $i = 365.25*86400*($start-1970);
+ $offset = 36000+rand(10000,60000);
+ $max = 365*$yrs*86400;
+ $lastyear = 0;
+
+ // we generate a timestamp, convert it to a date, and convert it back to a timestamp
+ // and check if the roundtrip broke the original timestamp value.
+ print "Testing $start to ".($start+$yrs).", or $max seconds, offset=$offset: ";
+ $cnt = 0;
+ for ($max += $i; $i < $max; $i += $offset) {
+ $ret = adodb_date('m,d,Y,H,i,s',$i);
+ $arr = explode(',',$ret);
+ if ($lastyear != $arr[2]) {
+ $lastyear = $arr[2];
+ print " $lastyear ";
+ flush();
+ }
+ $newi = adodb_mktime($arr[3],$arr[4],$arr[5],$arr[0],$arr[1],$arr[2]);
+ if ($i != $newi) {
+ print "Error at $i, adodb_mktime returned $newi ($ret)";
+ $fail = true;
+ break;
+ }
+ $cnt += 1;
+ }
+ echo "Tested $cnt dates
";
+ if (!$fail) print "
Passed !
"; + else print "Failed :-(
"; +} + +/** + Returns day of week, 0 = Sunday,... 6=Saturday. + Algorithm from PEAR::Date_Calc +*/ +function adodb_dow($year, $month, $day) +{ +/* +Pope Gregory removed 10 days - October 5 to October 14 - from the year 1582 and +proclaimed that from that time onwards 3 days would be dropped from the calendar +every 400 years. + +Thursday, October 4, 1582 (Julian) was followed immediately by Friday, October 15, 1582 (Gregorian). +*/ + if ($year <= 1582) { + if ($year < 1582 || + ($year == 1582 && ($month < 10 || ($month == 10 && $day < 15)))) $greg_correction = 3; + else + $greg_correction = 0; + } else + $greg_correction = 0; + + if($month > 2) + $month -= 2; + else { + $month += 10; + $year--; + } + + $day = floor((13 * $month - 1) / 5) + + $day + ($year % 100) + + floor(($year % 100) / 4) + + floor(($year / 100) / 4) - 2 * + floor($year / 100) + 77 + $greg_correction; + + return $day - 7 * floor($day / 7); +} + + +/** + Checks for leap year, returns true if it is. No 2-digit year check. Also + handles julian calendar correctly. +*/ +function _adodb_is_leap_year($year) +{ + if ($year % 4 != 0) return false; + + if ($year % 400 == 0) { + return true; + // if gregorian calendar (>1582), century not-divisible by 400 is not leap + } else if ($year > 1582 && $year % 100 == 0 ) { + return false; + } + + return true; +} + + +/** + checks for leap year, returns true if it is. Has 2-digit year check +*/ +function adodb_is_leap_year($year) +{ + return _adodb_is_leap_year(adodb_year_digit_check($year)); +} + +/** + Fix 2-digit years. Works for any century. + Assumes that if 2-digit is more than 30 years in future, then previous century. +*/ +function adodb_year_digit_check($y) +{ + if ($y < 100) { + + $yr = (integer) date("Y"); + $century = (integer) ($yr /100); + + if ($yr%100 > 50) { + $c1 = $century + 1; + $c0 = $century; + } else { + $c1 = $century; + $c0 = $century - 1; + } + $c1 *= 100; + // if 2-digit year is less than 30 years in future, set it to this century + // otherwise if more than 30 years in future, then we set 2-digit year to the prev century. + if (($y + $c1) < $yr+30) $y = $y + $c1; + else $y = $y + $c0*100; + } + return $y; +} + +/** + get local time zone offset from GMT +*/ +function adodb_get_gmt_diff() +{ +static $TZ; + if (isset($TZ)) return $TZ; + + $TZ = mktime(0,0,0,1,2,1970,0) - gmmktime(0,0,0,1,2,1970,0); + return $TZ; +} + +/** + Returns an array with date info. +*/ +function adodb_getdate($d=false,$fast=false) +{ + if ($d === false) return getdate(); + if (!defined('ADODB_TEST_DATES')) { + if ((abs($d) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range + if (!defined('ADODB_NO_NEGATIVE_TS') || $d >= 0) // if windows, must be +ve integer + return @getdate($d); + } + } + return _adodb_getdate($d); +} + +/* +// generate $YRS table for _adodb_getdate() +function adodb_date_gentable($out=true) +{ + + for ($i=1970; $i >= 1600; $i-=10) { + $s = adodb_gmmktime(0,0,0,1,1,$i); + echo "$i => $s,Unable to EXPLAIN a non-select statement
'; + $save = $this->LogSQL(false); + if ($partial) { + $sqlq = $this->qstr($sql.'%'); + $arr = $this->GetArray("select distinct sql1 from $perf_table where sql1 like $sqlq"); + if ($arr) + { + foreach($arr as $row) + { + $sql = reset($row); + if (crc32($sql) == $partial) break; + } + } + } + $sql = str_replace('?',"''",$sql); + + if ($partial) + { + $sqlq = $this->qstr($sql.'%'); + $sql = $this->GetOne("select sql1 from $perf_table where sql1 like $sqlq"); + } + + $s = 'Explain: '.htmlentities($sql, ENT_QUOTES, 'UTF-8').'
'; + $rs = $this->Execute('EXPLAIN '.$sql); + $s .= rs2html($rs,false,false,false,false); + $this->LogSQL($save); + $s .= $this->Tracer($sql); + return $s; + } + + function Tables() + { + if (!$this->tablesSQL) return false; + + $rs = $this->Execute($this->tablesSQL); + if (!$rs) return false; + + $html = rs2html($rs,false,false,false,false); + return $html; + } + + + function CreateLogTable() + { + if (!$this->createTableSQL) return false; + + $savelog = $this->LogSQL(false); + $ok = $this->Execute($this->createTableSQL); + $this->LogSQL($savelog); + return ($ok) ? true : false; + } + + + function GetReads() + { + global $ADODB_FETCH_MODE; + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + + $rs = $this->Execute('show status'); + + $ADODB_FETCH_MODE = $save; + + if (!$rs) return 0; + $val = 0; + while (!$rs->EOF) + { + switch($rs->fields[0]) + { + case 'Com_select': + $val = $rs->fields[1]; + $rs->Close(); + return $val; + } + $rs->MoveNext(); + } + + $rs->Close(); + return $val; + } + + function GetWrites() + { + global $ADODB_FETCH_MODE; + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + + $rs = $this->Execute('show status'); + + $ADODB_FETCH_MODE = $save; + + if (!$rs) return 0; + $val = 0.0; + while (!$rs->EOF) + { + switch($rs->fields[0]) + { + case 'Com_insert': + $val += $rs->fields[1]; + break; + case 'Com_delete': + $val += $rs->fields[1]; + break; + case 'Com_update': + $val += $rs->fields[1]/2; + $rs->Close(); + return $val; + } + $rs->MoveNext(); + } + + $rs->Close(); + return $val; + } + + function FindDBHitRatio() + { + global $ADODB_FETCH_MODE; + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + + $rs = $this->Execute('show table status'); + + $ADODB_FETCH_MODE = $save; + + if (!$rs) return ''; + $type = strtoupper($rs->fields[1]); + $rs->Close(); + switch($type) + { + case 'MYISAM': + case 'ISAM': + return $this->DBParameter('MyISAM cache hit ratio').' (MyISAM)'; + case 'INNODB': + return $this->DBParameter('InnoDB cache hit ratio').' (InnoDB)'; + default: + return $type.' not supported'; + } + } + + function GetQHitRatio() + { + //Total number of queries = Qcache_inserts + Qcache_hits + Qcache_not_cached + $hits = $this->_DBParameter(array("show status","Qcache_hits")); + $total = $this->_DBParameter(array("show status","Qcache_inserts")); + $total += $this->_DBParameter(array("show status","Qcache_not_cached")); + + $total += $hits; + if ($total) return round(($hits*100)/$total,2); + return 0; + } + + /* + Use session variable to store Hit percentage, because MySQL + does not remember last value of SHOW INNODB STATUS hit ratio + + # 1st query to SHOW INNODB STATUS + 0.00 reads/s, 0.00 creates/s, 0.00 writes/s + Buffer pool hit rate 1000 / 1000 + + # 2nd query to SHOW INNODB STATUS + 0.00 reads/s, 0.00 creates/s, 0.00 writes/s + No buffer pool activity since the last printout + */ + function GetInnoDBHitRatio() + { + global $ADODB_FETCH_MODE; + + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + + $rs = $this->Execute('show innodb status'); +; + $ADODB_FETCH_MODE = $save; + + if (!$rs || $rs->EOF) return 0; + $stat = $rs->fields[0]; + $rs->Close(); + $at = strpos($stat,'Buffer pool hit rate'); + $stat = substr($stat,$at,200); + if (preg_match('!Buffer pool hit rate\s*([0-9]*) / ([0-9]*)!',$stat,$arr)) + { + $val = 100*$arr[1]/$arr[2]; + $_SESSION['INNODB_HIT_PCT'] = $val; + return round($val,2); + } + else + { + if (isset($_SESSION['INNODB_HIT_PCT'])) return $_SESSION['INNODB_HIT_PCT']; + return 0; + } + return 0; + } + + function GetKeyHitRatio() + { + $hits = $this->_DBParameter(array("show status","Key_read_requests")); + $reqs = $this->_DBParameter(array("show status","Key_reads")); + if ($reqs == 0) return 0; + + return round(($hits/($reqs+$hits))*100,2); + } + + // start hack + var $optimizeTableLow = 'CHECK TABLE %s FAST QUICK'; + var $optimizeTableHigh = 'OPTIMIZE TABLE %s'; + + /** + * @see adodb_perf#optimizeTable + */ + function optimizeTable( $table, $mode = ADODB_OPT_LOW) + { + if ( !is_string( $table)) return false; + + $sql = ''; + switch( $mode) + { + case ADODB_OPT_LOW: + $sql = $this->optimizeTableLow; + break; + case ADODB_OPT_HIGH: + $sql = $this->optimizeTableHigh; + break; + default: + { + // May dont use __FUNCTION__ constant for BC (__FUNCTION__ Added in PHP 4.3.0) + trigger_error(sprintf( "%s: '%s' using of undefined mode '%s'
", __CLASS__, __FUNCTION__, $mode), E_USER_ERROR); + return false; + } + } + $sql = sprintf( $sql, $table); + + return $this->Execute( $sql) !== false; + } + +} + + + +eval('class mysql_perfmon_resultset_EXTENDER extends '. $last_module . '_ResultSet { }'); + +class mysql_perfmon_ResultSet extends mysql_perfmon_resultset_EXTENDER +{ + + function MetaType($t,$len=-1,$fieldobj=false) + { + if (is_object($t)) { + $fieldobj = $t; + $t = $fieldobj->type; + $len = $fieldobj->max_length; + } + + $len = -1; // mysql max_length is not accurate + switch (strtoupper($t)) { + case 'STRING': + case 'CHAR': + case 'VARCHAR': + case 'TINYBLOB': + case 'TINYTEXT': + case 'ENUM': + case 'SET': + if ($len <= $this->blobSize) return 'C'; + + case 'TEXT': + case 'LONGTEXT': + case 'MEDIUMTEXT': + return 'X'; + + // php_mysql extension always returns 'blob' even if 'text' + // so we have to check whether binary... + case 'IMAGE': + case 'LONGBLOB': + case 'BLOB': + case 'MEDIUMBLOB': + return !empty($fieldobj->binary) ? 'B' : 'X'; + + case 'YEAR': + case 'DATE': return 'D'; + + case 'TIME': + case 'DATETIME': + case 'TIMESTAMP': return 'T'; + + case 'INT': + case 'INTEGER': + case 'BIGINT': + case 'TINYINT': + case 'MEDIUMINT': + case 'SMALLINT': + + if (!empty($fieldobj->primary_key)) return 'R'; + else return 'I'; + + default: return 'N'; + } + } + +} + +?> diff --git a/lib/adodb/adodbSQL_drivers/mysql/mysql_transaction_module.inc b/lib/adodb/adodbSQL_drivers/mysql/mysql_transaction_module.inc new file mode 100644 index 0000000..3bb5a2a --- /dev/null +++ b/lib/adodb/adodbSQL_drivers/mysql/mysql_transaction_module.inc @@ -0,0 +1,113 @@ +transOff > 0) { + $this->transOff += 1; + return; + } + + $this->transaction_status = true; + + if ($this->debug && $this->transCnt > 0) + ADOConnection::outp("Bad Transaction: StartTrans called within BeginTrans"); + + $this->BeginTrans(); + $this->transOff = 1; + } + + function BeginTrans() + { + return false; + } + + function CompleteTrans($autoComplete = true) + { + if ($this->transOff > 1) { + $this->transOff -= 1; + return true; + } + + $this->transOff = 0; + if ($this->transaction_status && $autoComplete) { + if (!$this->CommitTrans()) { + $this->transaction_status = false; + if ($this->debug) + ADOConnection::outp("Smart Commit failed"); + } else + if ($this->debug) + ADOConnection::outp("Smart Commit occurred"); + } else { + $this->transaction_status = false; + $this->RollbackTrans(); + if ($this->debug) + ADOCOnnection::outp("Smart Rollback occurred"); + } + + return $this->transaction_status; + } + + function CommitTrans($ok=true) + { + return true; + } + + function RollbackTrans() + { + return false; + } + + function FailTrans() + { + if ($this->debug) + if ($this->transOff == 0) { + ADOConnection::outp("FailTrans outside StartTrans/CompleteTrans"); + } else { + ADOConnection::outp("FailTrans was called"); + } + $this->transaction_status = false; + } + + function HasFailedTrans() + { + if ($this->transOff > 0) + return $this->transaction_status == false; + + return false; + } + + function RowLock($table,$where) + { + return false; + } + + function CommitLock($table) + { + return $this->CommitTrans(); + } + + function RollbackLock($table) + { + return $this->RollbackTrans(); + } +} + +eval('class mysql_transaction_resultset_EXTENDER extends '. $last_module . '_ResultSet { }'); + +class mysql_transaction_ResultSet extends mysql_transaction_resultset_EXTENDER +{ +} +?> \ No newline at end of file diff --git a/lib/adodb/adodbSQL_drivers/mysqli/mysqli_datadict.inc b/lib/adodb/adodbSQL_drivers/mysqli/mysqli_datadict.inc new file mode 100644 index 0000000..5db473f --- /dev/null +++ b/lib/adodb/adodbSQL_drivers/mysqli/mysqli_datadict.inc @@ -0,0 +1,112 @@ +alterTableAddIndex) $sql[] = "ALTER TABLE $tabname DROP INDEX $idxname"; + else $sql[] = sprintf($this->dropIndex, $idxname, $tabname); + + if ( isset($idxoptions['DROP']) ) + return $sql; + } + + if ( empty ($flds) ) { + return $sql; + } + + if (isset($idxoptions['FULLTEXT'])) { + $unique = ' FULLTEXT'; + } elseif (isset($idxoptions['UNIQUE'])) { + $unique = ' UNIQUE'; + } else { + $unique = ''; + } + + if ( is_array($flds) ) $flds = implode(', ',$flds); + + if ($this->alterTableAddIndex) $s = "ALTER TABLE $tabname ADD $unique INDEX $idxname "; + else $s = 'CREATE' . $unique . ' INDEX ' . $idxname . ' ON ' . $tabname; + + $s .= ' (' . $flds . ')'; + + if ( isset($idxoptions[$this->upperName]) ) + $s .= $idxoptions[$this->upperName]; + + $sql[] = $s; + + return $sql; + } + +} + +?> \ No newline at end of file diff --git a/lib/adodb/adodbSQL_drivers/mysqli/mysqli_date_module.inc b/lib/adodb/adodbSQL_drivers/mysqli/mysqli_date_module.inc new file mode 100644 index 0000000..17a4668 --- /dev/null +++ b/lib/adodb/adodbSQL_drivers/mysqli/mysqli_date_module.inc @@ -0,0 +1,321 @@ +_Execute("select $this->sysTimeStamp"); + if ($rs && !$rs->EOF) + return $this->UnixTimeStamp(reset($rs->fields)); + else return false; + } + + function OffsetDate($dayFraction, $date=false) + { + if (!$date) + $date = $this->sysDate; + + $fraction = $dayFraction * 24 * 3600; + return "from_unixtime(unix_timestamp($date)+$fraction)"; + } + + function SetDateLocale($locale = 'En') + { + $this->locale = $locale; + switch (strtoupper($locale)) + { + case 'EN': + $this->fmtDate="'Y-m-d'"; + $this->fmtTimeStamp = "'Y-m-d H:i:s'"; + break; + + case 'US': + $this->fmtDate = "'m-d-Y'"; + $this->fmtTimeStamp = "'m-d-Y H:i:s'"; + break; + + case 'NL': + case 'FR': + case 'RO': + case 'IT': + $this->fmtDate="'d-m-Y'"; + $this->fmtTimeStamp = "'d-m-Y H:i:s'"; + break; + + case 'GE': + $this->fmtDate="'d.m.Y'"; + $this->fmtTimeStamp = "'d.m.Y H:i:s'"; + break; + + default: + $this->fmtDate="'Y-m-d'"; + $this->fmtTimeStamp = "'Y-m-d H:i:s'"; + break; + } + } + + function DBDate($date) + { + if (empty($date) && $date !== 0) + return 'null'; + + if (is_string($date) && !is_numeric($date)) { + if ($date === 'null' || strncmp($date, "'", 1) === 0) + return $date; + + if ($this->isoDates) + return "'$date'"; + + $date = $this->UnixDate($date); + } + return adodb_date($this->fmtDate, $date); + } + + function DBTimeStamp($timestamp) + { + if (empty($timestamp) && $timestamp !== 0) + return 'null'; + + # strlen(14) allows YYYYMMDDHHMMSS format + if (!is_string($timestamp) || (is_numeric($timestamp) && strlen($timestamp)<14)) + return adodb_date($this->fmtTimeStamp, $timestamp); + + if ($timestamp === 'null') + return $timestamp; + + if ($this->isoDates && strlen($timestamp) !== 14) + return "'$timestamp'"; + + return adodb_date($this->fmtTimeStamp, $this->UnixTimeStamp($timestamp)); + } + + function UnixDate($v) + { + if (is_object($v)) { + // odbtp support + //( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 ) + return adodb_mktime($v->hour, $v->minute, $v->second, $v->month, $v->day, $v->year); + } + if (is_numeric($v) && strlen($v) !== 8) + return $v; + + if (!preg_match( "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})|", ($v), $rr)) + return false; + + if ($rr[1] <= TIMESTAMP_FIRST_YEAR) + return 0; + + // h-m-s-MM-DD-YY + return adodb_mktime(0, 0, 0, $rr[2], $rr[3], $rr[1]); + } + + + function UnixTimeStamp($v) + { + if (is_object($v)) { + // odbtp support + //( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 ) + return adodb_mktime($v->hour, $v->minute, $v->second, $v->month, $v->day, $v->year); + } + + if (!preg_match("|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ ,-]*(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,4}))?|", ($v), $rr)) + return false; + + if ($rr[1] <= TIMESTAMP_FIRST_YEAR && $rr[2]<= 1) + return 0; + + // h-m-s-MM-DD-YY + if (!isset($rr[5])) + return adodb_mktime(0, 0, 0, $rr[2], $rr[3], $rr[1]); + + return adodb_mktime($rr[5], $rr[6], $rr[7], $rr[2], $rr[3], $rr[1]); + } + + function UserDate($v, $fmt='Y-m-d', $gmt=false) + { + $tt = $this->UnixDate($v); + + // $tt == -1 if pre TIMESTAMP_FIRST_YEAR + if (($tt === false || $tt == -1) && $v != false) + return $v; + else if ($tt == 0) + return $this->emptyDate; + else if ($tt == -1) { // pre-TIMESTAMP_FIRST_YEAR + } + + return ($gmt) ? adodb_gmdate($fmt, $tt) : adodb_date($fmt, $tt); + } + + function UserTimeStamp($v, $fmt='Y-m-d H:i:s', $gmt=false) + { + if (!isset($v)) + return $this->emptyTimeStamp; + + # strlen(14) allows YYYYMMDDHHMMSS format + if (is_numeric($v) && strlen($v)<14) + return ($gmt) ? adodb_gmdate($fmt, $v) : adodb_date($fmt,$v); + + $tt = $this->UnixTimeStamp($v); + // $tt == -1 if pre TIMESTAMP_FIRST_YEAR + if (($tt === false || $tt == -1) && $v != false) + return $v; + + if ($tt == 0) + return $this->emptyTimeStamp; + + return ($gmt) ? adodb_gmdate($fmt, $tt) : adodb_date($fmt, $tt); + } + + function SQLDate($fmt, $col=false) + { + if (!$col) + $col = $this->sysTimeStamp; + + $s = 'DATE_FORMAT('.$col.",'"; + $concat = false; + $len = strlen($fmt); + for ($i=0; $i < $len; $i++) { + $ch = $fmt[$i]; + switch($ch) { + default: + if ($ch == '\\') { + $i++; + $ch = substr($fmt, $i, 1); + } + + /** FALL THROUGH */ + case '-': + case '/': + $s .= $ch; + break; + + case 'Y': + case 'y': + $s .= '%Y'; + break; + + case 'M': + $s .= '%b'; + break; + + case 'm': + $s .= '%m'; + break; + + case 'D': + case 'd': + $s .= '%d'; + break; + + case 'Q': + case 'q': + $s .= "'),Quarter($col)"; + if ($len > $i+1) + $s .= ",DATE_FORMAT($col,'"; + else $s .= ",('"; + $concat = true; + break; + + case 'H': + $s .= '%H'; + break; + + case 'h': + $s .= '%I'; + break; + + case 'i': + $s .= '%i'; + break; + + case 's': + $s .= '%s'; + break; + + case 'a': + case 'A': + $s .= '%p'; + break; + + case 'w': + $s .= '%w'; + break; + + case 'l': + $s .= '%W'; + break; + } + } + $s.="')"; + if ($concat) + $s = "CONCAT($s)"; + + return $s; + } +} + +eval('class mysqli_date_resultset_EXTENDER extends '. $last_module . '_ResultSet { }'); + +class mysqli_date_ResultSet extends mysqli_date_resultset_EXTENDER +{ + var $emptyTimeStamp = ' '; /// what to display when $time==0 + var $emptyDate = ' '; /// what to display when $time==0 + var $datetime = false; + + function UserTimeStamp($v, $fmt='Y-m-d H:i:s') + { + if (is_numeric($v) && strlen($v)<14) + return adodb_date($fmt, $v); + + $tt = $this->UnixTimeStamp($v); + // $tt == -1 if pre TIMESTAMP_FIRST_YEAR + if (($tt === false || $tt == -1) && $v != false) + return $v; + + if ($tt === 0) + return $this->emptyTimeStamp; + else return adodb_date($fmt, $tt); + } + + function UserDate($v,$fmt='Y-m-d') + { + $tt = $this->UnixDate($v); + // $tt == -1 if pre TIMESTAMP_FIRST_YEAR + if (($tt === false || $tt == -1) && $v != false) + return $v; + else if ($tt == 0) + return $this->emptyDate; + else if ($tt == -1) { // pre-TIMESTAMP_FIRST_YEAR + } + return adodb_date($fmt, $tt); + } + + function UnixDate($v) + { + return mysqli_date_ADOConnection::UnixDate($v); + } + + function UnixTimeStamp($v) + { + return mysqli_date_ADOConnection::UnixTimeStamp($v); + } +} +?> \ No newline at end of file diff --git a/lib/adodb/adodbSQL_drivers/mysqli/mysqli_driver.inc b/lib/adodb/adodbSQL_drivers/mysqli/mysqli_driver.inc new file mode 100644 index 0000000..9f04690 --- /dev/null +++ b/lib/adodb/adodbSQL_drivers/mysqli/mysqli_driver.inc @@ -0,0 +1,641 @@ +dbtype = 'mysqli'; + $this->dataProvider = 'mysql'; + } + + /** + * Connection to database server and selected database + * + * @access private + */ + + function _connect($host = "", $username = "", $password = "", $database = "", $persistent, $forcenew) + { + if (!function_exists('mysqli_real_connect')) { + echo "ERROR: Function 'mysqli_real_connect' not found! Check if MySQL is properly set up. And that PHP has the MySQLi extension installed. If MySQLi is not installed, try setting KU_DBTYPE to 'mysql' and try again."; + return false; + } + + $this->host = $host; + $this->username = $username; + $this->password = $password; + $this->database = $database; + $this->persistent = $persistent; + $this->forcenewconnection = $forcenew; + + $this->connectionId = @mysqli_init(); + @mysqli_real_connect( $this->connectionId, $this->host, $this->username, $this->password, $this->database, $this->port, $this->socket, $this->clientFlags ); + + if (mysqli_connect_errno() != 0) + { + $this->connectionId = false; + } + + if ($this->connectionId === false) + { + if ($fn = $this->raiseErrorFn) + $fn($this->dbtype, 'CONNECT', $this->ErrorNo(), $this->ErrorMsg(), $this->host, $this->database, $this); + return false; + } + + if (!empty($this->database)) + { + if($this->SelectDB( $this->database ) == false) + { + $this->connectionId = false; + return false; + } + } + + return true; + } + + /** + * Choose a database to connect. + * + * @param dbname is the name of the database to select + * @return true or false + * @access public + */ + + function SelectDB($dbname) + { + $this->database = $dbname; + + if ($this->connectionId === false) + { + $this->connectionId = false; + return false; + } + else + { + $result = @mysqli_select_db( $this->connectionId, $this->database ); + + if($result === false) + { + if($this->createdatabase == true) + { + $result = @mysqli_query( $this->connectionId, "CREATE DATABASE IF NOT EXISTS " . $this->database ); + if ($result === false) { // error handling if query fails + return false; + } + $result = @mysqli_select_db( $this->connectionId, $this->database ); + if($result === false) + { + return false; + } + } + else + { + return false; + } + } + return true; + } + } + + /** + * Return database error message + * Usage: $errormessage =& $db->ErrorMsg(); + * + * @access public + */ + + function ErrorMsg() + { + if ($this->connectionId === false) + { + return @mysqli_connect_error(); + } + else + { + return @mysqli_error($this->connectionId); + } + } + + /** + * Return database error number + * Usage: $errorbo =& $db->ErrorNo(); + * + * @access public + */ + + function ErrorNo() + { + if ($this->connectionId === false) + { + return @mysqli_connect_errno(); + } + else + { + return @mysqli_errno($this->connectionId); + } + } + + /** + * Returns # of affected rows from insert/delete/update query + * + * @access public + * @return integer Affected rows + */ + + function Affected_Rows() + { + return @mysqli_affected_rows($this->connectionId); + } + + /** + * Returns the last record id of an inserted item + * Usage: $db->Insert_ID(); + * + * @access public + */ + + function Insert_ID() + { + return @mysqli_insert_id($this->connectionId); + } + + /** + * Correctly quotes a string so that all strings are escape coded. + * An example is $db->qstr("Haven't a clue."); + * + * @param string the string to quote + * @param [magic_quotes] if $s is GET/POST var, set to get_magic_quotes_gpc(). + * + * @return single-quoted string IE: 'Haven\'t a clue.' + */ + + function qstr($string, $magic_quotes=false) + { + if (!$magic_quotes) { + if (strnatcmp(PHP_VERSION, '4.3.0') >= 0 && function_exists('mysqli_real_escape_string')) { + return "'" . mysqli_real_escape_string($this->connectionId, $string) . "'"; + } + $string = str_replace("'", "\\'" , str_replace('\\', '\\\\', str_replace("\0", "\\\0", $string))); + return "'" . $string . "'"; + } + return "'" . str_replace('\\"', '"', $string) . "'"; + } + + function QMagic($string) + { + return $this->qstr($string, get_magic_quotes_gpc()); + } + + /** + * Returns concatenated string + * Usage: $db->Concat($str1,$str2); + * + * @return concatenated string + */ + function Concat() + { + $arr = func_get_args(); + $list = implode(', ', $arr); + + if (strlen($list) > 0) return "CONCAT($list)"; + else return ''; + } + + function IfNull( $field, $ifNull ) + { + return " IFNULL($field, $ifNull) "; + } + + /** + * Closes database connection + * Usage: $db->close(); + * + * @access public + */ + + function Close() + { + @mysqli_close( $this->connectionId ); + $this->connectionId = false; + } + + /** + * Returns All Records in an array + * + * Usage: $db->GetAll($sql); + * @access public + */ + + function &GetAll($sql, $inputarr = false) + { + $data =& $this->GetArray($sql, $inputarr); + return $data; + } + + /** + * Returns All Records in an array + * + * Usage: $db->GetArray($sql); + * @access public + */ + + function &GetArray($sql, $inputarr = false) + { + $data = false; + $result =& $this->Execute($sql, $inputarr); + if ($result) + { + $data =& $result->GetArray(); + $result->Close(); + } + return $data; + } + + /** + * Executes SQL query and instantiates resultset methods + * + * @access private + * @return mixed Resultset methods + */ + + function &do_query( $sql, $offset, $nrows, $inputarr=false ) + { + global $ADODB_FETCH_MODE; + + $false = false; + + $limit = ''; + if ($offset >= 0 || $nrows >= 0) + { + $offset = ($offset >= 0) ? $offset . "," : ''; + $nrows = ($nrows >= 0) ? $nrows : '18446744073709551615'; + $limit = ' LIMIT ' . $offset . ' ' . $nrows; + } + + + if ($inputarr && is_array($inputarr)) { + $sqlarr = explode('?', $sql); + if (!is_array(reset($inputarr))) $inputarr = array($inputarr); + foreach($inputarr as $arr) { + $sql = ''; $i = 0; + foreach($arr as $v) { + $sql .= $sqlarr[$i]; + switch(gettype($v)){ + case 'string': + $sql .= $this->qstr($v); + break; + case 'double': + $sql .= str_replace(',', '.', $v); + break; + case 'boolean': + $sql .= $v ? 1 : 0; + break; + default: + if ($v === null) + $sql .= 'NULL'; + else $sql .= $v; + } + $i += 1; + } + $sql .= $sqlarr[$i]; + if ($i+1 != sizeof($sqlarr)) + return $false; + $this->sql = $sql . $limit; + $time_start = array_sum(explode(' ', microtime())); + $this->query_count++; + $resultId = @mysqli_query($this->connectionId, $this->sql ); + $time_total = (array_sum(explode(' ', microtime())) - $time_start); + $this->query_time_total += $time_total; + if($this->debug_console) + { + $this->query_list[] = $this->sql; + $this->query_list_time[] = $time_total; + $this->query_list_errors[] = $this->ErrorMsg(); + } + if($this->debug) + { + $this->outp($sql . $limit); + } + if ($resultId === false) { // error handling if query fails + if ($fn = $this->raiseErrorFn) + $fn($this->dbtype, 'EXECUTE', $this->ErrorNo(), $this->ErrorMsg(), $this->sql, $inputarr, $this); + return $false; + } + } + } + else + { + $this->sql = $sql . $limit; + $time_start = array_sum(explode(' ', microtime())); + $this->query_count++; + $resultId = @mysqli_query($this->connectionId, $this->sql ); + $time_total = (array_sum(explode(' ', microtime())) - $time_start); + $this->query_time_total += $time_total; + if($this->debug_console) + { + $this->query_list[] = $this->sql; + $this->query_list_time[] = $time_total; + $this->query_list_errors[] = $this->ErrorMsg(); + } + if($this->debug) + { + $this->outp($sql . $limit); + } + } + + if ($resultId === false) { // error handling if query fails + if ($fn = $this->raiseErrorFn) + $fn($this->dbtype, 'EXECUTE', $this->ErrorNo(), $this->ErrorMsg(), $this->sql, $inputarr, $this); + return $false; + } + + if ($resultId === true) { // return simplified recordset for inserts/updates/deletes with lower overhead + $recordset = new ADORecordSet_empty(); + return $recordset; + } + + $resultset_name = $this->last_module_name . "_ResultSet"; + $recordset = new $resultset_name( $resultId, $this->connectionId ); + + $recordset->_currentRow = 0; + + switch ($ADODB_FETCH_MODE) + { + case ADODB_FETCH_NUM: $recordset->fetchMode = MYSQLI_NUM; break; + case ADODB_FETCH_ASSOC:$recordset->fetchMode = MYSQLI_ASSOC; break; + default: + case ADODB_FETCH_DEFAULT: + case ADODB_FETCH_BOTH:$recordset->fetchMode = MYSQLI_BOTH; break; + } + + $recordset->_numOfRows = @mysqli_num_rows( $resultId ); + if( $recordset->_numOfRows == 0) + { + $recordset->EOF = true; + } + $recordset->_numOfFields = @mysqli_num_fields( $resultId ); + $recordset->_fetch(); + + return $recordset; + } +} + +class mysqli_driver_ResultSet +{ + var $connectionId; + var $fields; + var $resultId; + var $_currentRow = 0; + var $_numOfRows = -1; + var $_numOfFields = -1; + var $fetchMode; + var $EOF; + + /** + * mysqlResultSet Constructor + * + * @access private + * @param string $record + * @param string $resultId + */ + + function mysqli_driver_ResultSet( $resultId, $connectionId ) + { + $this->fields = array(); + $this->connectionId = $connectionId; + $this->record = array(); + $this->resultId = $resultId; + $this->EOF = false; + } + + /** + * Frees resultset + * + * @access public + */ + + function Close() + { + @mysqli_free_result( $this->resultId ); + $this->fields = array(); + $this->resultId = false; + } + + /** + * Returns field name from select query + * + * @access public + * @param string $field + * @return string Field name + */ + + function fields( $field ) + { + if(empty($field)) + { + return $this->fields; + } + else + { + return $this->fields[$field]; + } + } + + /** + * Returns numrows from select query + * + * @access public + * @return integer Numrows + */ + + function RecordCount() + { + return $this->_numOfRows; + } + + /** + * Returns num of fields from select query + * + * @access public + * @return integer numfields + */ + + function FieldCount() + { + return $this->_numOfFields; + } + + /** + * Returns next record + * + * @access public + */ + + function MoveNext() + { + if (@$this->fields = mysqli_fetch_array($this->resultId,$this->fetchMode)) { + $this->_currentRow += 1; + return true; + } + if (!$this->EOF) { + $this->_currentRow += 1; + $this->EOF = true; + } + return false; + } + + /** + * Move to the first row in the recordset. Many databases do NOT support this. + * + * @return true or false + */ + + function MoveFirst() + { + if ($this->_currentRow == 0) return true; + return $this->Move(0); + } + + /** + * Returns the Last Record + * + * @access public + */ + + function MoveLast() + { + if ($this->EOF) return false; + return $this->Move($this->_numOfRows - 1); + } + + /** + * Random access to a specific row in the recordset. Some databases do not support + * access to previous rows in the databases (no scrolling backwards). + * + * @param rowNumber is the row to move to (0-based) + * + * @return true if there still rows available, or false if there are no more rows (EOF). + */ + + function Move($rowNumber = 0) + { + if ($rowNumber == $this->_currentRow) return true; + $this->EOF = false; + if ($this->_numOfRows > 0){ + if ($rowNumber >= $this->_numOfRows - 1){ + $rowNumber = $this->_numOfRows - 1; + } + } + + if ($this->_seek($rowNumber)) { + $this->_currentRow = $rowNumber; + if ($this->_fetch()) { + return true; + } + $this->fields = false; + } + $this->EOF = true; + return false; + } + + /** + * Perform Seek to specific row + * + * @access private + */ + + function _seek($row) + { + if ($this->_numOfRows == 0) return false; + return @mysqli_data_seek($this->resultId,$row); + } + + /** + * Fills field array with first database element when query initially executed + * + * @access private + */ + + function _fetch() + { + $this->fields = @mysqli_fetch_array($this->resultId,$this->fetchMode); + return is_array($this->fields); + } + + /** + * Check to see if last record reached + * + * @access public + */ + + function EOF() + { + if( $this->_currentRow < $this->_numOfRows) + { + return false; + } + else + { + $this->EOF = true; + return true; + } + } + + /** + * Returns All Records in an array + * + * @access public + * @param [nRows] is the number of rows to return. -1 means every row. + */ + + function &GetArray($nRows = -1) + { + $results = array(); + $cnt = 0; + while (!$this->EOF && $nRows != $cnt) { + $results[] = $this->fields; + $this->MoveNext(); + $cnt++; + } + return $results; + } + + function &GetRows($nRows = -1) + { + $arr =& $this->GetArray($nRows); + return $arr; + } + + function &GetAll($nRows = -1) + { + $arr =& $this->GetArray($nRows); + return $arr; + } + + /** + * Fetch field information for a table. + * + * @return object containing the name, type and max_length + */ + function FetchField($fieldOffset = -1) + { + // $fieldOffset not supported by mysqli + $fieldObject = @mysqli_fetch_field($this->resultId); + return $fieldObject; + } +} +?> \ No newline at end of file diff --git a/lib/adodb/adodbSQL_drivers/mysqli/mysqli_extend_module.inc b/lib/adodb/adodbSQL_drivers/mysqli/mysqli_extend_module.inc new file mode 100644 index 0000000..ada6ea9 --- /dev/null +++ b/lib/adodb/adodbSQL_drivers/mysqli/mysqli_extend_module.inc @@ -0,0 +1,150 @@ +Execute($sql, $inputarr); + if ($result) { + $data =& $result->GetAssoc($force_array, $first2cols); + $result->Close(); + } + return $data; + } + + /** + * Generates a sequence id and stores it in $this->genID; + * GenID is only available if $this->hasGenID = true; + * + * @param seqname name of sequence to use + * @param startID if sequence does not exist, start at this ID + * @return 0 if not supported, otherwise a sequence id + */ + + var $_genIDSQL = "update %s set id=LAST_INSERT_ID(id+1);"; + var $_genSeqSQL = "create table %s (id int not null)"; + var $_genSeq2SQL = "insert into %s values (%s)"; + var $_dropSeqSQL = "drop table %s"; + var $genID = 0; + + function GenID($seqname='adodbseq', $startID=1) + { + $getnext = sprintf($this->_genIDSQL, $seqname); + $holdtransOK = $this->transaction_status; + $result = @$this->Execute($getnext); + if (!$result) { + if ($holdtransOK) + $this->transaction_status = true; +// $u = strtoupper($seqname); + $this->Execute(sprintf($this->_genSeqSQL, $seqname)); + $this->Execute(sprintf($this->_genSeq2SQL, $seqname, $startID-1)); + $result = $this->Execute($getnext); + } + $this->genID = mysqli_insert_id($this->connectionId); + + if ($result) + $result->Close(); + + return $this->genID; + } + + function CreateSequence($seqname='adodbseq',$startID=1) + { + $u = strtoupper($seqname); + + $ok = $this->Execute(sprintf($this->_genSeqSQL, $seqname)); + if (!$ok) + return false; + return $this->Execute(sprintf($this->_genSeq2SQL, $seqname, $startID-1)); + } + + function DropSequence($seqname='adodbseq') + { + return $this->Execute(sprintf($this->_dropSeqSQL, $seqname)); + } + +} + +eval('class mysqli_extend_resultset_EXTENDER extends '. $last_module . '_ResultSet { }'); + +class mysqli_extend_ResultSet extends mysqli_extend_resultset_EXTENDER +{ + function &GetAssoc($force_array = false, $first2cols = false) + { + $results = false; + + if ($this->_numOfFields > 1) { + $numIndex = isset($this->fields[0]); + $results = array(); + if (!$first2cols && ($this->_numOfFields > 2 || $force_array)) { + if ($numIndex) { + while (!$this->EOF) { + $results[trim($this->fields[0])] = array_slice($this->fields, 1); + $this->MoveNext(); + } + } else { + while (!$this->EOF) { + $results[trim(reset($this->fields))] = array_slice($this->fields, 1); + $this->MoveNext(); + } + } + } else { + if ($numIndex) { + while (!$this->EOF) { + $results[trim(($this->fields[0]))] = $this->fields[1]; + $this->MoveNext(); + } + } else { + while (!$this->EOF) { + $v1 = trim(reset($this->fields)); + $v2 = ''.next($this->fields); + $results[$v1] = $v2; + $this->MoveNext(); + } + } + } + } + return $results; + } + + function PO_RecordCount($table="", $condition="") + { + $lnumrows = $this->_numOfRows; + if($lnumrows == -1 && $this->connectionId) + { + if($table) + { + if ($condition) + $condition = " WHERE " . $condition; + $resultrows = &$this->connectionId->Execute("SELECT COUNT(*) FROM $table $condition"); + if ($resultrows) + $lnumrows = reset($resultrows->fields); + } + } + return $lnumrows; + } + + function CurrentRow() + { + return $this->_currentRow; + } + + function AbsolutePosition() + { + return $this->_currentRow; + } + + function NextRecordSet() + { + return false; + } + +} +?> \ No newline at end of file diff --git a/lib/adodb/adodbSQL_drivers/mysqli/mysqli_meta_module.inc b/lib/adodb/adodbSQL_drivers/mysqli/mysqli_meta_module.inc new file mode 100644 index 0000000..ab17637 --- /dev/null +++ b/lib/adodb/adodbSQL_drivers/mysqli/mysqli_meta_module.inc @@ -0,0 +1,624 @@ +ErrorNo(); + + return adodb_error($this->dataProvider,$this->databaseType,$err); + } + + function MetaErrorMsg($errno) + { + include_once(ADODB_DIR . "/adodb-error.inc.php"); + return adodb_errormsg($errno); + } + + /** + * @returns an array with the primary key columns in it. + */ + function MetaPrimaryKeys($table, $owner=false) + { + // owner not used in base class - see oci8 + $p = array(); + $objs =& $this->MetaColumns($table); + if ($objs) { + foreach($objs as $v) { + if (!empty($v->primary_key)) + $p[] = $v->name; + } + } + if (sizeof($p)) + return $p; + if (function_exists('ADODB_VIEW_PRIMARYKEYS')) + return ADODB_VIEW_PRIMARYKEYS($this->databaseType, $this->database, $table, $owner); + return false; + } + + /** + * @returns assoc array where keys are tables, and values are foreign keys + */ + function MetaForeignKeys( $table, $owner = FALSE, $upper = FALSE, $associative = FALSE ) + { + if ( !empty($owner) ) { + $table = "$owner.$table"; + } + $a_create_table = $this->getRow(sprintf('SHOW CREATE TABLE %s', $table)); + if ($associative) $create_sql = $a_create_table["Create Table"]; + else $create_sql = $a_create_table[1]; + + $matches = array(); + + if (!preg_match_all("/FOREIGN KEY \(`(.*?)`\) REFERENCES `(.*?)` \(`(.*?)`\)/", $create_sql, $matches)) return false; + $foreign_keys = array(); + $num_keys = count($matches[0]); + for ( $i = 0; $i < $num_keys; $i ++ ) { + $my_field = explode('`, `', $matches[1][$i]); + $ref_table = $matches[2][$i]; + $ref_field = explode('`, `', $matches[3][$i]); + + if ( $upper ) { + $ref_table = strtoupper($ref_table); + } + + $foreign_keys[$ref_table] = array(); + $num_fields = count($my_field); + for ( $j = 0; $j < $num_fields; $j ++ ) { + if ( $associative ) { + $foreign_keys[$ref_table][$ref_field[$j]] = $my_field[$j]; + } else { + $foreign_keys[$ref_table][] = "{$my_field[$j]}={$ref_field[$j]}"; + } + } + } + + return $foreign_keys; + } + + // not the fastest implementation - quick and dirty - jlim + // for best performance, use the actual $rs->MetaType(). + function MetaType($t,$len=-1,$fieldobj=false) + { + if (empty($this->_metars)) { + $rsclass = $this->last_module_name . "_ResultSet"; + $this->_metars =& new $rsclass(false, $this->fetchMode); + } + + return $this->_metars->MetaType($t,$len,$fieldobj); + } + + /** + * return the databases that the driver can connect to. + * Some databases will return an empty array. + * + * @return an array of database names. + */ + function &MetaDatabases() + { + $qid = mysql_list_dbs($this->connectionId); + $arr = array(); + $i = 0; + $max = mysql_num_rows($qid); + while ($i < $max) { + $db = mysql_tablename($qid,$i); + if ($db != 'mysql') $arr[] = $db; + $i += 1; + } + return $arr; + } + + /** + * @param ttype can either be 'VIEW' or 'TABLE' or false. + * If false, both views and tables are returned. + * "VIEW" returns only views + * "TABLE" returns only tables + * @param showSchema returns the schema/user with the table name, eg. USER.TABLE + * @param mask is the input mask - only supported by oci8 and postgresql + * + * @return array of tables for current database. + */ + function &MetaTables($ttype=false,$showSchema=false,$mask=false) + { + $save = $this->metaTablesSQL; + if ($showSchema && is_string($showSchema)) { + $this->metaTablesSQL .= " from $showSchema"; + } + + if ($mask) { + $mask = $this->qstr($mask); + $this->metaTablesSQL .= " like $mask"; + } + $ret =& $this->_MetaTables($ttype,$showSchema); + + $this->metaTablesSQL = $save; + return $ret; + } + + function &_MetaTables($ttype=false,$showSchema=false,$mask=false) + { + global $ADODB_FETCH_MODE; + + $false = false; + if ($mask) { + return $false; + } + if ($this->metaTablesSQL) { + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + + if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); + + $rs = $this->Execute($this->metaTablesSQL); + if (isset($savem)) $this->SetFetchMode($savem); + $ADODB_FETCH_MODE = $save; + + if ($rs === false) return $false; + $arr =& $rs->GetArray(); + $arr2 = array(); + + if ($hast = ($ttype && isset($arr[0][1]))) { + $showt = strncmp($ttype,'T',1); + } + + for ($i=0; $i < sizeof($arr); $i++) { + if ($hast) { + if ($showt == 0) { + if (strncmp($arr[$i][1],'T',1) == 0) $arr2[] = trim($arr[$i][0]); + } else { + if (strncmp($arr[$i][1],'V',1) == 0) $arr2[] = trim($arr[$i][0]); + } + } else + $arr2[] = trim($arr[$i][0]); + } + $rs->Close(); + return $arr2; + } + return $false; + } + + function _findschema(&$table,&$schema) + { + if (!$schema && ($at = strpos($table,'.')) !== false) { + $schema = substr($table,0,$at); + $table = substr($table,$at+1); + } + } + + /** + * List columns in a database as an array of ADOFieldObjects. + * See top of file for definition of object. + * + * @param $table table name to query + * @param $normalize makes table name case-insensitive (required by some databases) + * @schema is optional database schema to use - not supported by all databases. + * + * @return array of ADOFieldObjects for current table. + */ + function MetaColumns($table) + { + $this->_findschema($table,$schema); + if ($schema) { + $dbName = $this->database; + $this->connection->SelectDB($schema); + } + global $ADODB_FETCH_MODE; + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + + if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); + $rs = $this->Execute(sprintf($this->metaColumnsSQL,$table)); + + if ($schema) { + $this->SelectDB($dbName); + } + + if (isset($savem)) $this->SetFetchMode($savem); + $ADODB_FETCH_MODE = $save; + if (!is_object($rs)) { + $false = false; + return $false; + } + + $retarr = array(); + while (!$rs->EOF){ + $fld = new ADOFieldObject(); + $fld->name = $rs->fields[0]; + $type = $rs->fields[1]; + + // split type into type(length): + $fld->scale = null; + if (preg_match("/^(.+)\((\d+),(\d+)/", $type, $query_array)) { + $fld->type = $query_array[1]; + $fld->max_length = is_numeric($query_array[2]) ? $query_array[2] : -1; + $fld->scale = is_numeric($query_array[3]) ? $query_array[3] : -1; + } elseif (preg_match("/^(.+)\((\d+)/", $type, $query_array)) { + $fld->type = $query_array[1]; + $fld->max_length = is_numeric($query_array[2]) ? $query_array[2] : -1; + } elseif (preg_match("/^(enum)\((.*)\)$/i", $type, $query_array)) { + $fld->type = $query_array[1]; + $arr = explode(",",$query_array[2]); + $fld->enums = $arr; + $zlen = max(array_map("strlen",$arr)) - 2; // PHP >= 4.0.6 + $fld->max_length = ($zlen > 0) ? $zlen : 1; + } else { + $fld->type = $type; + $fld->max_length = -1; + } + $fld->not_null = ($rs->fields[2] != 'YES'); + $fld->primary_key = ($rs->fields[3] == 'PRI'); + $fld->auto_increment = (strpos($rs->fields[5], 'auto_increment') !== false); + $fld->binary = (strpos($type,'blob') !== false); + $fld->unsigned = (strpos($type,'unsigned') !== false); + + if (!$fld->binary) { + $d = $rs->fields[4]; + if ($d != '' && $d != 'NULL') { + $fld->has_default = true; + $fld->default_value = $d; + } else { + $fld->has_default = false; + } + } + + if ($save == ADODB_FETCH_NUM) { + $retarr[] = $fld; + } else { + $retarr[strtoupper($fld->name)] = $fld; + } + $rs->MoveNext(); + } + + $rs->Close(); + return $retarr; + } + + /** + * List indexes on a table as an array. + * @param table table name to query + * @param primary true to only show primary keys. Not actually used for most databases + * + * @return array of indexes on current table. Each element represents an index, and is itself an associative array. + + Array ( + [name_of_index] => Array + ( + [unique] => true or false + [columns] => Array + ( + [0] => firstname + [1] => lastname + ) + ) + */ + function MetaIndexes ($table, $primary = FALSE, $owner=false) + { + // save old fetch mode + global $ADODB_FETCH_MODE; + + $false = false; + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + if ($this->fetchMode !== FALSE) { + $savem = $this->SetFetchMode(FALSE); + } + + // get index details + $rs =& $this->Execute(sprintf('SHOW INDEX FROM %s',$table)); + + // restore fetchmode + if (isset($savem)) { + $this->SetFetchMode($savem); + } + $ADODB_FETCH_MODE = $save; + + if (!is_object($rs)) { + return $false; + } + + $indexes = array (); + + // parse index data into array + while (!$rs->EOF) + { + if ($primary == FALSE AND $rs->fields[2] == 'PRIMARY') { + $rs->MoveNext(); + continue; + } + + if (!isset($indexes[$rs->fields[2]])) { + $indexes[$rs->fields[2]] = array( + 'unique' => ($rs->fields[1] == 0), + 'columns' => array() + ); + } + + $indexes[$rs->fields[2]]['columns'][$rs->fields[3] - 1] = $rs->fields[4]; + + $rs->MoveNext(); + } + + // sort columns by order in the index + foreach ( array_keys ($indexes) as $index ) + { + ksort ($indexes[$index]['columns']); + } + + return $indexes; + } + + /** + * List columns names in a table as an array. + * @param table table name to query + * + * @return array of column names for current table. + */ + function &MetaColumnNames($table, $numIndexes=false) + { + $objarr =& $this->MetaColumns($table); + if (!is_array($objarr)) { + $false = false; + return $false; + } + $arr = array(); + if ($numIndexes) { + $i = 0; + foreach($objarr as $v) $arr[$i++] = $v->name; + } else + foreach($objarr as $v) $arr[strtoupper($v->name)] = $v->name; + + return $arr; + } + + function MetaTransaction($mode,$db) + { + $mode = strtoupper($mode); + $mode = str_replace('ISOLATION LEVEL ','',$mode); + + switch($mode) { + + case 'READ UNCOMMITTED': + switch($db) { + case 'oci8': + case 'oracle': + return 'ISOLATION LEVEL READ COMMITTED'; + default: + return 'ISOLATION LEVEL READ UNCOMMITTED'; + } + break; + + case 'READ COMMITTED': + return 'ISOLATION LEVEL READ COMMITTED'; + break; + + case 'REPEATABLE READ': + switch($db) { + case 'oci8': + case 'oracle': + return 'ISOLATION LEVEL SERIALIZABLE'; + default: + return 'ISOLATION LEVEL REPEATABLE READ'; + } + break; + + case 'SERIALIZABLE': + return 'ISOLATION LEVEL SERIALIZABLE'; + break; + + default: + return $mode; + } + } + +} + +eval('class mysqli_meta_resultset_EXTENDER extends '. $last_module . '_ResultSet { }'); + +class mysqli_meta_ResultSet extends mysqli_meta_resultset_EXTENDER +{ + /** + * Get the metatype of the column. This is used for formatting. This is because + * many databases use different names for the same type, so we transform the original + * type to our standardised version which uses 1 character codes: + * + * @param t is the type passed in. Normally is ADOFieldObject->type. + * @param len is the maximum length of that field. This is because we treat character + * fields bigger than a certain size as a 'B' (blob). + * @param fieldobj is the field object returned by the database driver. Can hold + * additional info (eg. primary_key for mysql). + * + * @return the general type of the data: + * C for character < 250 chars + * X for teXt (>= 250 chars) + * B for Binary + * N for numeric or floating point + * D for date + * T for timestamp + * L for logical/Boolean + * I for integer + * R for autoincrement counter/integer + * + * + */ + function MetaType($t,$len=-1,$fieldobj=false) + { + if (is_object($t)) { + $fieldobj = $t; + $t = $fieldobj->type; + $len = $fieldobj->max_length; + } + + $len = -1; // mysql max_length is not accurate + switch (strtoupper($t)) { + case 'STRING': + case 'CHAR': + case 'VARCHAR': + case 'TINYBLOB': + case 'TINYTEXT': + case 'ENUM': + case 'SET': + if ($len <= $this->blobSize) return 'C'; + + case 'TEXT': + case 'LONGTEXT': + case 'MEDIUMTEXT': + return 'X'; + + // php_mysql extension always returns 'blob' even if 'text' + // so we have to check whether binary... + case 'IMAGE': + case 'LONGBLOB': + case 'BLOB': + case 'MEDIUMBLOB': + return !empty($fieldobj->binary) ? 'B' : 'X'; + + case 'YEAR': + case 'DATE': return 'D'; + + case 'TIME': + case 'DATETIME': + case 'TIMESTAMP': return 'T'; + + case 'INT': + case 'INTEGER': + case 'BIGINT': + case 'TINYINT': + case 'MEDIUMINT': + case 'SMALLINT': + + if (!empty($fieldobj->primary_key)) return 'R'; + else return 'I'; + + default: + static $typeMap = array( + 'VARCHAR' => 'C', + 'VARCHAR2' => 'C', + 'CHAR' => 'C', + 'C' => 'C', + 'STRING' => 'C', + 'NCHAR' => 'C', + 'NVARCHAR' => 'C', + 'VARYING' => 'C', + 'BPCHAR' => 'C', + 'CHARACTER' => 'C', + 'INTERVAL' => 'C', # Postgres + 'MACADDR' => 'C', # postgres + ## + 'LONGCHAR' => 'X', + 'TEXT' => 'X', + 'NTEXT' => 'X', + 'M' => 'X', + 'X' => 'X', + 'CLOB' => 'X', + 'NCLOB' => 'X', + 'LVARCHAR' => 'X', + ## + 'BLOB' => 'B', + 'IMAGE' => 'B', + 'BINARY' => 'B', + 'VARBINARY' => 'B', + 'LONGBINARY' => 'B', + 'B' => 'B', + ## + 'YEAR' => 'D', // mysql + 'DATE' => 'D', + 'D' => 'D', + ## + 'UNIQUEIDENTIFIER' => 'C', # MS SQL Server + ## + 'TIME' => 'T', + 'TIMESTAMP' => 'T', + 'DATETIME' => 'T', + 'TIMESTAMPTZ' => 'T', + 'T' => 'T', + 'TIMESTAMP WITHOUT TIME ZONE' => 'T', // postgresql + ## + 'BOOL' => 'L', + 'BOOLEAN' => 'L', + 'BIT' => 'L', + 'L' => 'L', + ## + 'COUNTER' => 'R', + 'R' => 'R', + 'SERIAL' => 'R', // ifx + 'INT IDENTITY' => 'R', + ## + 'INT' => 'I', + 'INT2' => 'I', + 'INT4' => 'I', + 'INT8' => 'I', + 'INTEGER' => 'I', + 'INTEGER UNSIGNED' => 'I', + 'SHORT' => 'I', + 'TINYINT' => 'I', + 'SMALLINT' => 'I', + 'I' => 'I', + ## + 'LONG' => 'N', // interbase is numeric, oci8 is blob + 'BIGINT' => 'N', // this is bigger than PHP 32-bit integers + 'DECIMAL' => 'N', + 'DEC' => 'N', + 'REAL' => 'N', + 'DOUBLE' => 'N', + 'DOUBLE PRECISION' => 'N', + 'SMALLFLOAT' => 'N', + 'FLOAT' => 'N', + 'NUMBER' => 'N', + 'NUM' => 'N', + 'NUMERIC' => 'N', + 'MONEY' => 'N', + + ## informix 9.2 + 'SQLINT' => 'I', + 'SQLSERIAL' => 'I', + 'SQLSMINT' => 'I', + 'SQLSMFLOAT' => 'N', + 'SQLFLOAT' => 'N', + 'SQLMONEY' => 'N', + 'SQLDECIMAL' => 'N', + 'SQLDATE' => 'D', + 'SQLVCHAR' => 'C', + 'SQLCHAR' => 'C', + 'SQLDTIME' => 'T', + 'SQLINTERVAL' => 'N', + 'SQLBYTES' => 'B', + 'SQLTEXT' => 'X', + ## informix 10 + "SQLINT8" => 'I8', + "SQLSERIAL8" => 'I8', + "SQLNCHAR" => 'C', + "SQLNVCHAR" => 'C', + "SQLLVARCHAR" => 'X', + "SQLBOOL" => 'L' + ); + + $tmap = false; + $t = strtoupper($t); + $tmap = (isset($typeMap[$t])) ? $typeMap[$t] : 'N'; + if ($t == 'LONG' && $this->dataProvider == 'oci8') return 'B'; + return $tmap; + } + } + +} + +?> \ No newline at end of file diff --git a/lib/adodb/adodbSQL_drivers/mysqli/mysqli_perfmon_module.inc b/lib/adodb/adodbSQL_drivers/mysqli/mysqli_perfmon_module.inc new file mode 100644 index 0000000..a9394ba --- /dev/null +++ b/lib/adodb/adodbSQL_drivers/mysqli/mysqli_perfmon_module.inc @@ -0,0 +1,468 @@ + array('RATIO', + '=GetKeyHitRatio', + '=WarnCacheRatio'), + 'InnoDB cache hit ratio' => array('RATIO', + '=GetInnoDBHitRatio', + '=WarnCacheRatio'), + 'data cache hit ratio' => array('HIDE', # only if called + '=FindDBHitRatio', + '=WarnCacheRatio'), + 'sql cache hit ratio' => array('RATIO', + '=GetQHitRatio', + ''), + 'IO', + 'data reads' => array('IO', + '=GetReads', + 'Number of selects (Key_reads is not accurate)'), + 'data writes' => array('IO', + '=GetWrites', + 'Number of inserts/updates/deletes * coef (Key_writes is not accurate)'), + + 'Data Cache', + 'MyISAM data cache size' => array('DATAC', + array("show variables", 'key_buffer_size'), + '' ), + 'BDB data cache size' => array('DATAC', + array("show variables", 'bdb_cache_size'), + '' ), + 'InnoDB data cache size' => array('DATAC', + array("show variables", 'innodb_buffer_pool_size'), + '' ), + 'Memory Usage', + 'read buffer size' => array('CACHE', + array("show variables", 'read_buffer_size'), + '(per session)'), + 'sort buffer size' => array('CACHE', + array("show variables", 'sort_buffer_size'), + 'Size of sort buffer (per session)' ), + 'table cache' => array('CACHE', + array("show variables", 'table_cache'), + 'Number of tables to keep open'), + 'Connections', + 'current connections' => array('SESS', + array('show status','Threads_connected'), + ''), + 'max connections' => array( 'SESS', + array("show variables",'max_connections'), + ''), + + false + ); + + /** + Get server version info... + + @returns An array with 2 elements: $arr['description'] is the description string, + and $arr['version'] is the version (also a string). + */ + function ServerInfo() + { + $arr = array(); + $result = $this->do_query('select version()', -1, -1, false); + if (!$result->EOF) $data = $result->fields; + else $data = array(); + $arr['version'] = $arr['description'] = $data[0]; + //****// Where does $arr come from in ADOdb - doesn't appear global, maybe null? A possible bug? + return $arr; + } + + /* + Explain Plan for $sql. + If only a snippet of the $sql is passed in, then $partial will hold the crc32 of the + actual sql. + */ + function Explain($sql,$partial=false) + { + $perf_table = perfmon_parent_ADOConnection::table(); // this was missing in the original ADOdb perf class - bug? + + if (strtoupper(substr(trim($sql),0,6)) !== 'SELECT') return 'Unable to EXPLAIN a non-select statement
'; + $save = $this->LogSQL(false); + if ($partial) { + $sqlq = $this->qstr($sql.'%'); + $arr = $this->GetArray("select distinct sql1 from $perf_table where sql1 like $sqlq"); + if ($arr) + { + foreach($arr as $row) + { + $sql = reset($row); + if (crc32($sql) == $partial) break; + } + } + } + $sql = str_replace('?',"''",$sql); + + if ($partial) + { + $sqlq = $this->qstr($sql.'%'); + $sql = $this->GetOne("select sql1 from $perf_table where sql1 like $sqlq"); + } + + $s = 'Explain: '.htmlentities($sql, ENT_QUOTES, 'UTF-8').'
'; + $rs = $this->Execute('EXPLAIN '.$sql); + $s .= rs2html($rs,false,false,false,false); + $this->LogSQL($save); + $s .= $this->Tracer($sql); + return $s; + } + + function Tables() + { + if (!$this->tablesSQL) return false; + + $rs = $this->Execute($this->tablesSQL); + if (!$rs) return false; + + $html = rs2html($rs,false,false,false,false); + return $html; + } + + + function CreateLogTable() + { + if (!$this->createTableSQL) return false; + + $savelog = $this->LogSQL(false); + $ok = $this->Execute($this->createTableSQL); + $this->LogSQL($savelog); + return ($ok) ? true : false; + } + + + function GetReads() + { + global $ADODB_FETCH_MODE; + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + + $rs = $this->Execute('show status'); + + $ADODB_FETCH_MODE = $save; + + if (!$rs) return 0; + $val = 0; + while (!$rs->EOF) + { + switch($rs->fields[0]) + { + case 'Com_select': + $val = $rs->fields[1]; + $rs->Close(); + return $val; + } + $rs->MoveNext(); + } + + $rs->Close(); + return $val; + } + + function GetWrites() + { + global $ADODB_FETCH_MODE; + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + + $rs = $this->Execute('show status'); + + $ADODB_FETCH_MODE = $save; + + if (!$rs) return 0; + $val = 0.0; + while (!$rs->EOF) + { + switch($rs->fields[0]) + { + case 'Com_insert': + $val += $rs->fields[1]; + break; + case 'Com_delete': + $val += $rs->fields[1]; + break; + case 'Com_update': + $val += $rs->fields[1]/2; + $rs->Close(); + return $val; + } + $rs->MoveNext(); + } + + $rs->Close(); + return $val; + } + + function FindDBHitRatio() + { + global $ADODB_FETCH_MODE; + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + + $rs = $this->Execute('show table status'); + + $ADODB_FETCH_MODE = $save; + + if (!$rs) return ''; + $type = strtoupper($rs->fields[1]); + $rs->Close(); + switch($type) + { + case 'MYISAM': + case 'ISAM': + return $this->DBParameter('MyISAM cache hit ratio').' (MyISAM)'; + case 'INNODB': + return $this->DBParameter('InnoDB cache hit ratio').' (InnoDB)'; + default: + return $type.' not supported'; + } + } + + function GetQHitRatio() + { + //Total number of queries = Qcache_inserts + Qcache_hits + Qcache_not_cached + $hits = $this->_DBParameter(array("show status","Qcache_hits")); + $total = $this->_DBParameter(array("show status","Qcache_inserts")); + $total += $this->_DBParameter(array("show status","Qcache_not_cached")); + + $total += $hits; + if ($total) return round(($hits*100)/$total,2); + return 0; + } + + /* + Use session variable to store Hit percentage, because MySQL + does not remember last value of SHOW INNODB STATUS hit ratio + + # 1st query to SHOW INNODB STATUS + 0.00 reads/s, 0.00 creates/s, 0.00 writes/s + Buffer pool hit rate 1000 / 1000 + + # 2nd query to SHOW INNODB STATUS + 0.00 reads/s, 0.00 creates/s, 0.00 writes/s + No buffer pool activity since the last printout + */ + function GetInnoDBHitRatio() + { + global $ADODB_FETCH_MODE; + + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + + $rs = $this->Execute('show innodb status'); +; + $ADODB_FETCH_MODE = $save; + + if (!$rs || $rs->EOF) return 0; + $stat = $rs->fields[0]; + $rs->Close(); + $at = strpos($stat,'Buffer pool hit rate'); + $stat = substr($stat,$at,200); + if (preg_match('!Buffer pool hit rate\s*([0-9]*) / ([0-9]*)!',$stat,$arr)) + { + $val = 100*$arr[1]/$arr[2]; + $_SESSION['INNODB_HIT_PCT'] = $val; + return round($val,2); + } + else + { + if (isset($_SESSION['INNODB_HIT_PCT'])) return $_SESSION['INNODB_HIT_PCT']; + return 0; + } + return 0; + } + + function GetKeyHitRatio() + { + $hits = $this->_DBParameter(array("show status","Key_read_requests")); + $reqs = $this->_DBParameter(array("show status","Key_reads")); + if ($reqs == 0) return 0; + + return round(($hits/($reqs+$hits))*100,2); + } + + // start hack + var $optimizeTableLow = 'CHECK TABLE %s FAST QUICK'; + var $optimizeTableHigh = 'OPTIMIZE TABLE %s'; + + /** + * @see adodb_perf#optimizeTable + */ + function optimizeTable( $table, $mode = ADODB_OPT_LOW) + { + if ( !is_string( $table)) return false; + + $sql = ''; + switch( $mode) + { + case ADODB_OPT_LOW: + $sql = $this->optimizeTableLow; + break; + case ADODB_OPT_HIGH: + $sql = $this->optimizeTableHigh; + break; + default: + { + // May dont use __FUNCTION__ constant for BC (__FUNCTION__ Added in PHP 4.3.0) + trigger_error(sprintf( "%s: '%s' using of undefined mode '%s'
", __CLASS__, __FUNCTION__, $mode), E_USER_ERROR); + return false; + } + } + $sql = sprintf( $sql, $table); + + return $this->Execute( $sql) !== false; + } + +} + + + +eval('class mysqli_perfmon_resultset_EXTENDER extends '. $last_module . '_ResultSet { }'); + +class mysqli_perfmon_ResultSet extends mysqli_perfmon_resultset_EXTENDER +{ + + function MetaType($t, $len = -1, $fieldobj = false) + { + if (is_object($t)) { + $fieldobj = $t; + $t = $fieldobj->type; + $len = $fieldobj->max_length; + } + + + $len = -1; // mysql max_length is not accurate + switch (strtoupper($t)) { + case 'STRING': + case 'CHAR': + case 'VARCHAR': + case 'TINYBLOB': + case 'TINYTEXT': + case 'ENUM': + case 'SET': + + case MYSQLI_TYPE_TINY_BLOB : + case MYSQLI_TYPE_CHAR : + case MYSQLI_TYPE_STRING : + case MYSQLI_TYPE_ENUM : + case MYSQLI_TYPE_SET : + case 253 : + if ($len <= $this->blobSize) return 'C'; + + case 'TEXT': + case 'LONGTEXT': + case 'MEDIUMTEXT': + return 'X'; + + + // php_mysql extension always returns 'blob' even if 'text' + // so we have to check whether binary... + case 'IMAGE': + case 'LONGBLOB': + case 'BLOB': + case 'MEDIUMBLOB': + + case MYSQLI_TYPE_BLOB : + case MYSQLI_TYPE_LONG_BLOB : + case MYSQLI_TYPE_MEDIUM_BLOB : + + return !empty($fieldobj->binary) ? 'B' : 'X'; + case 'YEAR': + case 'DATE': + case MYSQLI_TYPE_DATE : + case MYSQLI_TYPE_YEAR : + + return 'D'; + + case 'TIME': + case 'DATETIME': + case 'TIMESTAMP': + + case MYSQLI_TYPE_DATETIME : + case MYSQLI_TYPE_NEWDATE : + case MYSQLI_TYPE_TIME : + case MYSQLI_TYPE_TIMESTAMP : + + return 'T'; + + case 'INT': + case 'INTEGER': + case 'BIGINT': + case 'TINYINT': + case 'MEDIUMINT': + case 'SMALLINT': + + case MYSQLI_TYPE_INT24 : + case MYSQLI_TYPE_LONG : + case MYSQLI_TYPE_LONGLONG : + case MYSQLI_TYPE_SHORT : + case MYSQLI_TYPE_TINY : + + if (!empty($fieldobj->primary_key)) return 'R'; + + return 'I'; + + + // Added floating-point types + // Maybe not necessery. + case 'FLOAT': + case 'DOUBLE': + // case 'DOUBLE PRECISION': + case 'DECIMAL': + case 'DEC': + case 'FIXED': + default: + //if (!is_numeric($t)) echo "--- Error in type matching $t -----
"; + return 'N'; + } + } // function + +} + +?> diff --git a/lib/adodb/adodbSQL_drivers/mysqli/mysqli_transaction_module.inc b/lib/adodb/adodbSQL_drivers/mysqli/mysqli_transaction_module.inc new file mode 100644 index 0000000..ce57348 --- /dev/null +++ b/lib/adodb/adodbSQL_drivers/mysqli/mysqli_transaction_module.inc @@ -0,0 +1,138 @@ +transOff > 0) { + $this->transOff += 1; + return; + } + $this->transaction_status = true; + + if ($this->debug && $this->transCnt > 0) + ADOConnection::outp("Bad Transaction: StartTrans called within BeginTrans"); + + $this->BeginTrans(); + $this->transOff = 1; + } + + function BeginTrans() + { + if ($this->transOff) + return true; + + $this->transCnt += 1; + $this->Execute('SET AUTOCOMMIT=0'); + $this->Execute('BEGIN'); + return true; + } + + function CompleteTrans($autoComplete = true) + { + if ($this->transOff > 1) { + $this->transOff -= 1; + return true; + } + $this->transOff = 0; + if ($this->transaction_status && $autoComplete) { + if (!$this->CommitTrans()) { + $this->transaction_status = false; + if ($this->debug) + ADOConnection::outp("Smart Commit failed"); + } else + if ($this->debug) + ADOConnection::outp("Smart Commit occurred"); + } else { + $this->RollbackTrans(); + if ($this->debug) + ADOCOnnection::outp("Smart Rollback occurred"); + } + return $this->transaction_status; + } + + function CommitTrans($ok=true) + { + if ($this->transOff) + return true; + + if (!$ok) return + $this->RollbackTrans(); + + if ($this->transCnt) + $this->transCnt -= 1; + + $this->Execute('COMMIT'); + $this->Execute('SET AUTOCOMMIT=1'); + return true; + } + + function RollbackTrans() + { + if ($this->transOff) + return true; + + if ($this->transCnt) + $this->transCnt -= 1; + + $this->Execute('ROLLBACK'); + $this->Execute('SET AUTOCOMMIT=1'); + return true; + } + + function FailTrans() + { + if ($this->debug) + if ($this->transOff == 0) { + ADOConnection::outp("FailTrans outside StartTrans/CompleteTrans"); + } else { + ADOConnection::outp("FailTrans was called"); + } + $this->transaction_status = false; + } + + function HasFailedTrans() + { + if ($this->transOff > 0) + return $this->transaction_status == false; + + return false; + } + + function RowLock($tables,$where,$flds='1 as ignore') + { + if ($this->transCnt==0) + $this->BeginTrans(); + + return $this->GetOne("select $flds from $tables where $where for update"); + } + + function CommitLock($table) + { + return $this->CommitTrans(); + } + + function RollbackLock($table) + { + return $this->RollbackTrans(); + } + +} + +eval('class mysqli_transaction_resultset_EXTENDER extends '. $last_module . '_ResultSet { }'); + +class mysqli_transaction_ResultSet extends mysqli_transaction_resultset_EXTENDER +{ +} +?> \ No newline at end of file diff --git a/lib/adodb/adodbSQL_drivers/postgres64/postgres64_datadict.inc b/lib/adodb/adodbSQL_drivers/postgres64/postgres64_datadict.inc new file mode 100644 index 0000000..ea6e17c --- /dev/null +++ b/lib/adodb/adodbSQL_drivers/postgres64/postgres64_datadict.inc @@ -0,0 +1,211 @@ +TableName ($tabname); + $sql = array(); + list($lines,$pkey) = $this->_GenFields($flds); + $alter = 'ALTER TABLE ' . $tabname . $this->addCol . ' '; + foreach($lines as $v) { + if (($not_null = preg_match('/NOT NULL/i',$v))) { + $v = preg_replace('/NOT NULL/i','',$v); + } + if (preg_match('/^([^ ]+) .*(DEFAULT [^ ]+)/',$v,$matches)) { + list(,$colname,$default) = $matches; + $sql[] = $alter . str_replace($default,'',$v); + $sql[] = 'ALTER TABLE '.$tabname.' ALTER COLUMN '.$colname.' SET ' . $default; + } else { + $sql[] = $alter . $v; + } + if ($not_null) { + list($colname) = explode(' ',$v); + $sql[] = 'ALTER TABLE '.$tabname.' ALTER COLUMN '.$colname.' SET NOT NULL'; + } + } + return $sql; + } + + function AlterColumnSQL($tabname, $flds, $tableflds='',$tableoptions='') + { + if (!$tableflds) { + if ($this->debug) $this->outp("AlterColumnSQL needs a complete table-definiton for PostgreSQL"); + return array(); + } + return $this->_recreate_copy_table($tabname,False,$tableflds,$tableoptions); + } + + function DropColumnSQL($tabname, $flds, $tableflds='',$tableoptions='') + { + $has_drop_column = 7.3 <= (float) @$this->serverInfo['version']; + if (!$has_drop_column && !$tableflds) { + if ($this->debug) $this->outp("DropColumnSQL needs complete table-definiton for PostgreSQL < 7.3"); + return array(); + } + if ($has_drop_column) { + return ADODB_DataDict::DropColumnSQL($tabname, $flds); + } + return $this->_recreate_copy_table($tabname,$flds,$tableflds,$tableoptions); + } + + function DropTableSQL($tabname) + { + $sql = ADODB_DataDict::DropTableSQL($tabname); + $drop_seq = $this->_DropAutoIncrement($tabname); + if ($drop_seq) $sql[] = $drop_seq; + return $sql; + } + + function _CreateSuffix($fname, &$ftype, $fnotnull,$fdefault,$fautoinc,$fconstraint) + { + if ($fautoinc) { + $ftype = 'SERIAL'; + return ''; + } + $suffix = ''; + if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault"; + if ($fnotnull) $suffix .= ' NOT NULL'; + if ($fconstraint) $suffix .= ' '.$fconstraint; + return $suffix; + } + + function _DropAutoIncrement($tabname) + { + $tabname = $this->connection->qstr('%'.$tabname.'%'); + + $seq = $this->connection->GetOne("SELECT relname FROM pg_class WHERE NOT relname ~ 'pg_.*' AND relname LIKE $tabname AND relkind='S'"); + + // check if a tables depends on the sequenz and it therefor cant and dont need to be droped separatly + if (!$seq || $this->connection->GetOne("SELECT relname FROM pg_class JOIN pg_depend ON pg_class.relfilenode=pg_depend.objid WHERE relname='$seq' AND relkind='S' AND deptype='i'")) { + return False; + } + return "DROP SEQUENCE ".$seq; + } + + function _IndexSQL($idxname, $tabname, $flds, $idxoptions) + { + $sql = array(); + if ( isset($idxoptions['REPLACE']) || isset($idxoptions['DROP']) ) { + $sql[] = sprintf ($this->dropIndex, $idxname, $tabname); + if ( isset($idxoptions['DROP']) ) + return $sql; + } + if ( empty ($flds) ) { + return $sql; + } + + $unique = isset($idxoptions['UNIQUE']) ? ' UNIQUE' : ''; + $s = 'CREATE' . $unique . ' INDEX ' . $idxname . ' ON ' . $tabname . ' '; + if (isset($idxoptions['HASH'])) + $s .= 'USING HASH '; + + if ( isset($idxoptions[$this->upperName]) ) + $s .= $idxoptions[$this->upperName]; + + if ( is_array($flds) ) + $flds = implode(', ',$flds); + $s .= '(' . $flds . ')'; + $sql[] = $s; + return $sql; + } + + + function _recreate_copy_table($tabname,$dropflds,$tableflds,$tableoptions='') + { + if ($dropflds && !is_array($dropflds)) $dropflds = explode(',',$dropflds); + $copyflds = array(); + foreach($this->MetaColumns($tabname) as $fld) { + if (!$dropflds || !in_array($fld->name,$dropflds)) { + // we need to explicit convert varchar to a number to be able to do an AlterColumn of a char column to a nummeric one + if (preg_match('/'.$fld->name.' (I|I2|I4|I8|N|F)/i',$tableflds,$matches) && + in_array($fld->type,array('varchar','char','text','bytea'))) { + $copyflds[] = "to_number($fld->name,'S99D99')"; + } else { + $copyflds[] = $fld->name; + } + // identify the sequence name and the fld its on + if ($fld->primary_key && $fld->has_default && + preg_match("/nextval\('([^']+)'::text\)/",$fld->default_value,$matches)) { + $seq_name = $matches[1]; + $seq_fld = $fld->name; + } + } + } + $copyflds = implode(', ',$copyflds); + + $tempname = $tabname.'_tmp'; + $aSql[] = 'BEGIN'; // we use a transaction, to make sure not to loose the content of the table + $aSql[] = "SELECT * INTO TEMPORARY TABLE $tempname FROM $tabname"; + $aSql = array_merge($aSql,$this->DropTableSQL($tabname)); + $aSql = array_merge($aSql,$this->CreateTableSQL($tabname,$tableflds,$tableoptions)); + $aSql[] = "INSERT INTO $tabname SELECT $copyflds FROM $tempname"; + if ($seq_name && $seq_fld) { // if we have a sequence we need to set it again + $seq_name = $tabname.'_'.$seq_fld.'_seq'; // has to be the name of the new implicit sequence + $aSql[] = "SELECT setval('$seq_name',MAX($seq_fld)) FROM $tabname"; + } + $aSql[] = "DROP TABLE $tempname"; + // recreate the indexes, if they not contain one of the droped columns + foreach($this->MetaIndexes($tabname) as $idx_name => $idx_data) + { + if (substr($idx_name,-5) != '_pkey' && (!$dropflds || !count(array_intersect($dropflds,$idx_data['columns'])))) { + $aSql = array_merge($aSql,$this->CreateIndexSQL($idx_name,$tabname,$idx_data['columns'], + $idx_data['unique'] ? array('UNIQUE') : False)); + } + } + $aSql[] = 'COMMIT'; + return $aSql; + } +} + +?> \ No newline at end of file diff --git a/lib/adodb/adodbSQL_drivers/postgres64/postgres64_date_module.inc b/lib/adodb/adodbSQL_drivers/postgres64/postgres64_date_module.inc new file mode 100644 index 0000000..cd6a4a0 --- /dev/null +++ b/lib/adodb/adodbSQL_drivers/postgres64/postgres64_date_module.inc @@ -0,0 +1,316 @@ +_Execute("select $this->sysTimeStamp"); + if ($rs && !$rs->EOF) + return $this->UnixTimeStamp(reset($rs->fields)); + + return false; + } + + function OffsetDate($dayFraction, $date=false) + { + if (!$date) + $date = $this->sysDate; + else if (strncmp($date,"'",1) == 0) { + $len = strlen($date); + if (10 <= $len && $len <= 12) + $date = 'date ' . $date; + else $date = 'timestamp ' . $date; + } + return "($date+interval'$dayFraction days')"; + } + + function SetDateLocale($locale = 'En') + { + $this->locale = $locale; + switch (strtoupper($locale)) + { + case 'EN': + $this->fmtDate="'Y-m-d'"; + $this->fmtTimeStamp = "'Y-m-d H:i:s'"; + break; + + case 'US': + $this->fmtDate = "'m-d-Y'"; + $this->fmtTimeStamp = "'m-d-Y H:i:s'"; + break; + + case 'NL': + case 'FR': + case 'RO': + case 'IT': + $this->fmtDate="'d-m-Y'"; + $this->fmtTimeStamp = "'d-m-Y H:i:s'"; + break; + + case 'GE': + $this->fmtDate="'d.m.Y'"; + $this->fmtTimeStamp = "'d.m.Y H:i:s'"; + break; + + default: + $this->fmtDate="'Y-m-d'"; + $this->fmtTimeStamp = "'Y-m-d H:i:s'"; + break; + } + } + + function DBDate($date) + { + if (empty($date) && $date !== 0) + return 'null'; + + if (is_string($date) && !is_numeric($date)) { + if ($date === 'null' || strncmp($date, "'", 1) === 0) + return $date; + + if ($this->isoDates) + return "'$date'"; + + $date = $this->UnixDate($date); + } + + return adodb_date($this->fmtDate,$date); + } + + function DBTimeStamp($timestamp) + { + if (empty($timestamp) && $timestamp !== 0) + return 'null'; + + # strlen(14) allows YYYYMMDDHHMMSS format + if (!is_string($timestamp) || (is_numeric($timestamp) && strlen($timestamp)<14)) + return adodb_date($this->fmtTimeStamp, $timestamp); + + if ($timestamp === 'null') + return $timestamp; + + if ($this->isoDates && strlen($timestamp) !== 14) + return "'$timestamp'"; + + $timestamp = $this->UnixTimeStamp($timestamp); + return adodb_date($this->fmtTimeStamp, $timestamp); + } + + function UnixDate($v) + { + if (is_object($v)) { + // odbtp support + //( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 ) + return adodb_mktime($v->hour, $v->minute, $v->second, $v->month, $v->day, $v->year); + } + + if (is_numeric($v) && strlen($v) !== 8) + return $v; + + if (!preg_match( "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})|", ($v), $rr)) + return false; + + if ($rr[1] <= TIMESTAMP_FIRST_YEAR) + return 0; + + // h-m-s-MM-DD-YY + return adodb_mktime(0, 0, 0, $rr[2], $rr[3], $rr[1]); + } + + function UnixTimeStamp($v) + { + if (is_object($v)) { + // odbtp support + //( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 ) + return adodb_mktime($v->hour, $v->minute, $v->second, $v->month, $v->day, $v->year); + } + + if (!preg_match("|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ ,-]*(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,4}))?|", ($v), $rr)) + return false; + + if ($rr[1] <= TIMESTAMP_FIRST_YEAR && $rr[2]<= 1) + return 0; + + // h-m-s-MM-DD-YY + if (!isset($rr[5])) + return adodb_mktime(0, 0, 0, $rr[2], $rr[3], $rr[1]); + else return adodb_mktime($rr[5], $rr[6], $rr[7], $rr[2], $rr[3], $rr[1]); + } + + function UserDate($v, $fmt='Y-m-d', $gmt=false) + { + $tt = $this->UnixDate($v); + + // $tt == -1 if pre TIMESTAMP_FIRST_YEAR + if (($tt === false || $tt == -1) && $v != false) + return $v; + else if ($tt == 0) + return $this->emptyDate; + else if ($tt == -1) { // pre-TIMESTAMP_FIRST_YEAR + } + + return ($gmt) ? adodb_gmdate($fmt, $tt) : adodb_date($fmt, $tt); + } + + function UserTimeStamp($v, $fmt='Y-m-d H:i:s', $gmt=false) + { + if (!isset($v)) + return $this->emptyTimeStamp; + + # strlen(14) allows YYYYMMDDHHMMSS format + if (is_numeric($v) && strlen($v)<14) + return ($gmt) ? adodb_gmdate($fmt,$v) : adodb_date($fmt,$v); + + $tt = $this->UnixTimeStamp($v); + // $tt == -1 if pre TIMESTAMP_FIRST_YEAR + if (($tt === false || $tt == -1) && $v != false) + return $v; + + if ($tt == 0) + return $this->emptyTimeStamp; + else return ($gmt) ? adodb_gmdate($fmt,$tt) : adodb_date($fmt,$tt); + } + + function SQLDate($fmt, $col=false) + { + if (!$col) + $col = $this->sysTimeStamp; + + $s = 'TO_CHAR('.$col.",'"; + $len = strlen($fmt); + for ($i=0; $i < $len; $i++) { + $ch = $fmt[$i]; + switch($ch) { + case 'Y': + case 'y': + $s .= 'YYYY'; + break; + + case 'Q': + case 'q': + $s .= 'Q'; + break; + + case 'M': + $s .= 'Mon'; + break; + + case 'm': + $s .= 'MM'; + break; + + case 'D': + case 'd': + $s .= 'DD'; + break; + + case 'H': + $s.= 'HH24'; + break; + + case 'h': + $s .= 'HH'; + break; + + case 'i': + $s .= 'MI'; + break; + + case 's': + $s .= 'SS'; + break; + + case 'a': + case 'A': + $s .= 'AM'; + break; + + case 'w': + $s .= 'D'; + break; + + case 'l': + $s .= 'DAY'; + break; + + default: + // handle escape characters... + if ($ch == '\\') { + $i++; + $ch = substr($fmt,$i,1); + } + if (strpos('-/.:;, ',$ch) !== false) + $s .= $ch; + else $s .= '"'.$ch.'"'; + } + } + return $s. "')"; + } +} + +eval('class postgres64_date_resultset_EXTENDER extends '. $last_module . '_ResultSet { }'); + +class postgres64_date_ResultSet extends postgres64_date_resultset_EXTENDER +{ + var $emptyTimeStamp = ' '; /// what to display when $time==0 + var $emptyDate = ' '; /// what to display when $time==0 + var $datetime = false; + + function UserTimeStamp($v, $fmt='Y-m-d H:i:s') + { + if (is_numeric($v) && strlen($v)<14) + return adodb_date($fmt,$v); + + $tt = $this->UnixTimeStamp($v); + // $tt == -1 if pre TIMESTAMP_FIRST_YEAR + if (($tt === false || $tt == -1) && $v != false) + return $v; + + if ($tt === 0) + return $this->emptyTimeStamp; + else return adodb_date($fmt,$tt); + } + + function UserDate($v,$fmt='Y-m-d') + { + $tt = $this->UnixDate($v); + // $tt == -1 if pre TIMESTAMP_FIRST_YEAR + if (($tt === false || $tt == -1) && $v != false) + return $v; + else if ($tt == 0) + return $this->emptyDate; + else if ($tt == -1) { // pre-TIMESTAMP_FIRST_YEAR + } + return adodb_date($fmt,$tt); + } + + function UnixDate($v) + { + return postgres64_date_ADOConnection::UnixDate($v); + } + + function UnixTimeStamp($v) + { + return postgres64_date_ADOConnection::UnixTimeStamp($v); + } + +} +?> \ No newline at end of file diff --git a/lib/adodb/adodbSQL_drivers/postgres64/postgres64_driver.inc b/lib/adodb/adodbSQL_drivers/postgres64/postgres64_driver.inc new file mode 100644 index 0000000..fa90c93 --- /dev/null +++ b/lib/adodb/adodbSQL_drivers/postgres64/postgres64_driver.inc @@ -0,0 +1,659 @@ +dbtype = 'postgres64'; + $this->dataProvider = 'postgres'; + } + + /** + * Connection to database server and selected database + * + * @access private + */ + + function _connect($host = "", $username = "", $password = "", $database = "", $persistent, $forcenew) + { + if (!function_exists('pg_connect')) return false; + + $this->host = $host; + $this->username = $this->query_addslashes($username); + $this->password = $this->query_addslashes($password); + if (strlen($database) == 0) $database = 'template1'; + $this->database = $this->query_addslashes($database); + + if ($this->username || $this->password || $this->database) { + $this->connect_string = $this->host; + if ($this->connect_string) { + $host = split(":", $this->connect_string); + if ($host[0]) $this->connect_string = "host=" . $this->query_addslashes($host[0]); + else $this->connect_string = 'host=localhost'; + if (isset($host[1])) $this->connect_string .= " port=$host[1]"; + else if (!empty($this->port)) $this->connect_string .= " port=" . $this->port; + } + if ($this->username) $this->connect_string .= " user=" . $this->username; + if ($this->password) $this->connect_string .= " password=" . $this->password; + if ($this->database) $this->connect_string .= " dbname=" . $this->database; + } + else + { + $this->connect_string = $this->host; + } + + $this->persistent = $persistent; + $this->forcenewconnection = $forcenew; + + $this->_makeconnection(); + + if ($this->connectionId === false) + { + if ($fn = $this->raiseErrorFn) + $fn($this->dbtype, 'CONNECT', $this->ErrorNo(), $this->ErrorMsg(), $this->host, $this->database, $this); + return $this->SelectDB( $this->database ); + } + + return true; + } + + function _makeconnection() + { + if($this->persistent == 1) + { + $this->connectionId = @pg_pconnect( $this->connect_string ); + } + else + { + if(!$this->forcenewconnection) + { + $this->connectionId = @pg_connect( $this->connect_string ); + } + else + { + $this->connectionId = @pg_connect( $this->connect_string, PGSQL_CONNECT_FORCE_NEW ); + } + } + } + + function query_addslashes($query) + { + $len = strlen($query); + if ($len == 0) + return "''"; + if (strncmp($query,"'",1) === 0 && substr($query,$len-1) == "'") + return $s; + return "'".addslashes($query)."'"; + } + + /** + * Choose a database to connect. + * + * @param dbname is the name of the database to select + * @return true or false + * @access public + */ + + function SelectDB($dbname) + { + $this->database = $dbname; + + if ($this->connectionId === false) + { + if($this->createdatabase == true) + { + $this->connectionId = @pg_pconnect( "host=$this->host user=$this->username password=$this->password" ); + $result = @pg_query($this->connectionId, "CREATE DATABASE " . $this->database ); + if ($result === false) { // error handling if query fails + $this->connectionId = false; + return false; + } + + $this->_makeconnection(); + if($this->connectionId === false) + { + $this->connectionId = false; + return false; + } + else + { + return true; + } + } + $this->connectionId = false; + return false; + } + else + { + return true; + } + } + + /** + * Return database error message + * Usage: $errormessage =& $db->ErrorMsg(); + * + * @access public + */ + + function ErrorMsg() + { + return @pg_last_error($this->connectionId); + } + + /** + * Return database error number + * Usage: $errorbo =& $db->ErrorNo(); + * + * @access public + */ + + function ErrorNo() + { + $error = @pg_last_error( $this->connectionId ); + return strlen($error) ? $error : 0; + } + + /** + * Returns # of affected rows from insert/delete/update query + * + * @access public + * @return integer Affected rows + */ + + function Affected_Rows() + { + return @pg_affected_rows($this->record_set); + } + + /** + * Returns the last record id of an inserted item + * Usage: $db->Insert_ID(); + * + * @access public + */ + + function Insert_ID() + { + return @pg_getlastoid($this->record_set); + } + + /** + * Correctly quotes a string so that all strings are escape coded. + * An example is $db->qstr("Haven't a clue."); + * + * @param string the string to quote + * @param [magic_quotes] if $s is GET/POST var, set to get_magic_quotes_gpc(). + * + * @return single-quoted string IE: 'Haven\'t a clue.' + */ + + function qstr($string, $magic_quotes=false) + { + // Ugly hack alert! Repalce backticks with some arbitrary character + $string = str_replace('`', "\x1C", $string); + if (!$magic_quotes) { + $string = str_replace("'", "\\'", str_replace('\\', '\\\\', str_replace("\0", "\\\0", $string))); + return "'" . $string . "'"; + } + return "'" . str_replace('\\"', '"', $string) . "'"; + } + + function QMagic($string) + { + return $this->qstr($string, get_magic_quotes_gpc()); + } + + /** + * Returns concatenated string + * Usage: $db->Concat($str1,$str2); + * + * @return concatenated string + */ + function Concat() + { + $arr = func_get_args(); + return implode("||", $arr); + } + + function IfNull( $field, $ifNull ) + { + return " coalesce($field, $ifNull) "; + } + + /** + * Closes database connection + * Usage: $db->close(); + * + * @access public + */ + + function Close() + { + @pg_close( $this->connectionId ); + $this->connectionId = false; + } + + /** + * Returns All Records in an array + * + * Usage: $db->GetAll($sql); + * @access public + */ + + function &GetAll($sql, $inputarr = false) + { + $data =& $this->GetArray($sql, $inputarr); + return $data; + } + + /** + * Returns All Records in an array + * + * Usage: $db->GetArray($sql); + * @access public + */ + + function &GetArray($sql, $inputarr = false) + { + $data = false; + $result =& $this->Execute($sql, $inputarr); + if ($result) + { + $data =& $result->GetArray(); + $result->Close(); + } + return $data; + } + + /** + * Executes SQL query and instantiates resultset methods + * + * @access private + * @return mixed Resultset methods + */ + + function &do_query( $sql, $offset, $nrows, $inputarr=false ) + { + global $ADODB_FETCH_MODE; + $sql = str_replace('`', '"', $sql); + $sql = str_replace("\x1C", "`", $sql); + $sql = str_replace('IS_DELETED', 'is_deleted', $sql); + $sql = str_replace("HIGH_PRIORITY", "", $sql); + $false = false; + +// $limit = ''; +// if ($offset != -1 || $nrows != -1) +// { +// $offset = ($offset>=0) ? $offset . "," : ''; +// $limit = ' LIMIT ' . $offset . ' ' . $nrows; +// } + + if ($inputarr && is_array($inputarr)) { + $sqlarr = explode('?', $sql); + if (!is_array(reset($inputarr))) $inputarr = array($inputarr); + foreach($inputarr as $arr) { + $sql = ''; $i = 0; + foreach($arr as $v) { + $sql .= $sqlarr[$i]; + switch(gettype($v)){ + case 'string': + $sql .= $this->qstr($v); + break; + case 'double': + $sql .= str_replace(',', '.', $v); + break; + case 'boolean': + $sql .= $v ? "'t'" : "'f'"; + break; + default: + if ($v === null) + $sql .= 'NULL'; + else $sql .= $v; + } + $i += 1; + } + $sql .= $sqlarr[$i]; + if ($i+1 != sizeof($sqlarr)) + return $false; + $this->sql = $sql; + $time_start = array_sum(explode(' ', microtime())); + $this->query_count++; + $resultId = @pg_query( $this->sql ); + $time_total = (array_sum(explode(' ', microtime())) - $time_start); + $this->query_time_total += $time_total; + if($this->debug_console) + { + $this->query_list[] = $this->sql; + $this->query_list_time[] = $time_total; + $this->query_list_errors[] = $this->ErrorMsg(); + } + if($this->debug) + { + $this->outp($sql); + } + if ($resultId === false) { // error handling if query fails + if ($fn = $this->raiseErrorFn) + $fn($this->dbtype, 'EXECUTE', $this->ErrorNo(), $this->ErrorMsg(), $this->sql, $inputarr, $this); + return $false; + } + } + } + else + { + $this->sql = $sql; + $time_start = array_sum(explode(' ', microtime())); + $this->query_count++; + $resultId = @pg_query( $this->sql); + $time_total = (array_sum(explode(' ', microtime())) - $time_start); + $this->query_time_total += $time_total; + if($this->debug_console) + { + $this->query_list[] = $this->sql; + $this->query_list_time[] = $time_total; + $this->query_list_errors[] = $this->ErrorMsg(); + } + if($this->debug) + { + $this->outp($sql); + } + } + + if ($resultId === false) { // error handling if query fails + if ($fn = $this->raiseErrorFn) + $fn($this->dbtype, 'EXECUTE', $this->ErrorNo(), $this->ErrorMsg(), $this->sql, $inputarr, $this); + return $false; + } + + if (@pg_numfields( $resultId ) <= 0) { // return simplified recordset for inserts/updates/deletes with lower overhead + $recordset = new ADORecordSet_empty(); + $this->record_set = $recordset; + return $recordset; + } + + $resultset_name = $this->last_module_name . "_ResultSet"; + $recordset = new $resultset_name( $resultId, $this->connectionId ); + $this->record_set = $recordset; + + $recordset->_currentRow = 0; + + switch ($ADODB_FETCH_MODE) + { + case ADODB_FETCH_NUM: $recordset->fetchMode = PGSQL_NUM; break; + case ADODB_FETCH_ASSOC:$recordset->fetchMode = PGSQL_ASSOC; break; + default: + case ADODB_FETCH_DEFAULT: + case ADODB_FETCH_BOTH:$recordset->fetchMode = PGSQL_BOTH; break; + } + + $recordset->_numOfRows = @pg_numrows( $resultId ); + if( $recordset->_numOfRows == 0) + { + $recordset->EOF = true; + } + $recordset->_numOfFields = @pg_numfields( $resultId ); + $recordset->_fetch(); + + return $recordset; + } +} + +class postgres64_driver_ResultSet +{ + var $connectionId; + var $fields; + var $resultId; + var $_currentRow = 0; + var $_numOfRows = -1; + var $_numOfFields = -1; + var $fetchMode; + var $EOF; + + /** + * pgsqlResultSet Constructor + * + * @access private + * @param string $record + * @param string $resultId + */ + + function postgres64_driver_ResultSet( $resultId, $connectionId ) + { + $this->fields = array(); + $this->connectionId = $connectionId; + $this->record = array(); + $this->resultId = $resultId; + $this->EOF = false; + } + + /** + * Frees resultset + * + * @access public + */ + + function Close() + { + pg_free_result( $this->resultId ); + $this->fields = array(); + $this->resultId = false; + } + + /** + * Returns field name from select query + * + * @access public + * @param string $field + * @return string Field name + */ + + function fields( $field ) + { + if(empty($field)) + { + return $this->fields; + } + else + { + return $this->fields[$field]; + } + } + + /** + * Returns numrows from select query + * + * @access public + * @return integer Numrows + */ + + function RecordCount() + { + return $this->_numOfRows; + } + + /** + * Returns num of fields from select query + * + * @access public + * @return integer numfields + */ + + function FieldCount() + { + return $this->_numOfFields; + } + + /** + * Returns next record + * + * @access public + */ + + function MoveNext() + { + if (@$this->fields = pg_fetch_array($this->resultId, NULL, $this->fetchMode)) { + $this->_currentRow += 1; + return true; + } + if (!$this->EOF) { + $this->_currentRow += 1; + $this->EOF = true; + } + return false; + } + + /** + * Move to the first row in the recordset. Many databases do NOT support this. + * + * @return true or false + */ + + function MoveFirst() + { + if ($this->_currentRow == 0) return true; + return $this->Move(0); + } + + /** + * Returns the Last Record + * + * @access public + */ + + function MoveLast() + { + if ($this->EOF) return false; + return $this->Move($this->_numOfRows - 1); + } + + /** + * Random access to a specific row in the recordset. Some databases do not support + * access to previous rows in the databases (no scrolling backwards). + * + * @param rowNumber is the row to move to (0-based) + * + * @return true if there still rows available, or false if there are no more rows (EOF). + */ + + function Move($rowNumber = 0) + { + if ($rowNumber == $this->_currentRow) return true; + $this->EOF = false; + if ($this->_numOfRows > 0){ + if ($rowNumber >= $this->_numOfRows - 1){ + $rowNumber = $this->_numOfRows - 1; + } + } + + if ($this->_seek($rowNumber)) { + $this->_currentRow = $rowNumber; + if ($this->_fetch()) { + return true; + } + $this->fields = false; + } + $this->EOF = true; + return false; + } + + /** + * Perform Seek to specific row + * + * @access private + */ + + function _seek($row) + { + if ($this->_numOfRows == 0) return false; + return @pg_result_seek($this->resultId,$row); + } + + /** + * Fills field array with first database element when query initially executed + * + * @access private + */ + + function _fetch() + { + $this->fields = @pg_fetch_array($this->resultId, NULL, $this->fetchMode); + return is_array($this->fields); + } + + /** + * Check to see if last record reached + * + * @access public + */ + + function EOF() + { + if( $this->_currentRow < $this->_numOfRows) + { + return false; + } + else + { + $this->EOF = true; + return true; + } + } + + /** + * Returns All Records in an array + * + * @access public + * @param [nRows] is the number of rows to return. -1 means every row. + */ + + function &GetArray($nRows = -1) + { + $results = array(); + $cnt = 0; + while (!$this->EOF && $nRows != $cnt) { + $results[] = $this->fields; + $this->MoveNext(); + $cnt++; + } + return $results; + } + + function &GetRows($nRows = -1) + { + $arr =& $this->GetArray($nRows); + return $arr; + } + + function &GetAll($nRows = -1) + { + $arr =& $this->GetArray($nRows); + return $arr; + } + + /** + * Fetch field information for a table. + * + * @return object containing the name, type and max_length + */ + function FetchField($fieldOffset = -1) + { + $fieldObject= new ADOFieldObject(); + $fieldObject->name = @pg_fieldname($this->resultId, $fieldOffset); + $fieldObject->type = @pg_fieldtype($this->resultId, $fieldOffset); + $fieldObject->max_length = @pg_fieldsize($this->resultId, $fieldOffset); + return $fieldObject; + } +} +?> \ No newline at end of file diff --git a/lib/adodb/adodbSQL_drivers/postgres64/postgres64_extend_module.inc b/lib/adodb/adodbSQL_drivers/postgres64/postgres64_extend_module.inc new file mode 100644 index 0000000..4130663 --- /dev/null +++ b/lib/adodb/adodbSQL_drivers/postgres64/postgres64_extend_module.inc @@ -0,0 +1,147 @@ +Execute($sql, $inputarr); + if ($result) { + $data =& $result->GetAssoc($force_array, $first2cols); + $result->Close(); + } + return $data; + } + + /** + * Generates a sequence id and stores it in $this->genID; + * GenID is only available if $this->hasGenID = true; + * + * @param seqname name of sequence to use + * @param startID if sequence does not exist, start at this ID + * @return 0 if not supported, otherwise a sequence id + */ + + var $_genIDSQL = "SELECT NEXTVAL('%s')"; + var $_genSeqSQL = "CREATE SEQUENCE %s START %s"; + var $_dropSeqSQL = "DROP SEQUENCE %s"; + var $genID = 0; + + function GenID($seqname='adodbseq', $startID=1) + { + $getnext = sprintf($this->_genIDSQL, $seqname); + $holdtransOK = $this->transaction_status; + $save_handler = $this->raiseErrorFn; + $this->raiseErrorFn = ''; + @($result = $this->Execute($getnext)); + $this->raiseErrorFn = $save_handler; + + if (!$result) { + $this->transaction_status = $holdtransOK; + $createseq = $this->Execute(sprintf($this->_genSeqSQL, $seqname, $startID)); + $result = $this->Execute($getnext); + } + if ($result && !$result->EOF) + $this->genID = reset($result->fields); + else $this->genID = 0; + + if ($result) + $result->Close(); + + return $this->genID; + } + + function CreateSequence($seqname='adodbseq', $startID=1) + { + return $this->Execute(sprintf($this->_genSeqSQL, $seqname, $startID)); + } + + function DropSequence($seqname='adodbseq') + { + return $this->Execute(sprintf($this->_dropSeqSQL, $seqname)); + } + +} + +eval('class postgres64_extend_resultset_EXTENDER extends '. $last_module . '_ResultSet { }'); + +class postgres64_extend_ResultSet extends postgres64_extend_resultset_EXTENDER +{ + function &GetAssoc($force_array = false, $first2cols = false) + { + $results = false; + + if ($this->_numOfFields > 1) { + $numIndex = isset($this->fields[0]); + $results = array(); + if (!$first2cols && ($this->_numOfFields > 2 || $force_array)) { + if ($numIndex) { + while (!$this->EOF) { + $results[trim($this->fields[0])] = array_slice($this->fields, 1); + $this->MoveNext(); + } + } else { + while (!$this->EOF) { + $results[trim(reset($this->fields))] = array_slice($this->fields, 1); + $this->MoveNext(); + } + } + } else { + if ($numIndex) { + while (!$this->EOF) { + $results[trim(($this->fields[0]))] = $this->fields[1]; + $this->MoveNext(); + } + } else { + while (!$this->EOF) { + $v1 = trim(reset($this->fields)); + $v2 = ''.next($this->fields); + $results[$v1] = $v2; + $this->MoveNext(); + } + } + } + } + return $results; + } + + function PO_RecordCount($table="", $condition="") + { + $lnumrows = $this->_numOfRows; + if($lnumrows == -1 && $this->connectionId) + { + if($table) + { + if ($condition) + $condition = " WHERE " . $condition; + $resultrows = &$this->connectionId->Execute("SELECT COUNT(*) FROM $table $condition"); + if ($resultrows) + $lnumrows = reset($resultrows->fields); + } + } + return $lnumrows; + } + + function CurrentRow() + { + return $this->_currentRow; + } + + function AbsolutePosition() + { + return $this->_currentRow; + } + + function NextRecordSet() + { + return false; + } + +} +?> \ No newline at end of file diff --git a/lib/adodb/adodbSQL_drivers/postgres64/postgres64_meta_module.inc b/lib/adodb/adodbSQL_drivers/postgres64/postgres64_meta_module.inc new file mode 100644 index 0000000..02ac70f --- /dev/null +++ b/lib/adodb/adodbSQL_drivers/postgres64/postgres64_meta_module.inc @@ -0,0 +1,684 @@ + 0 AND a.atttypid = t.oid AND a.attrelid = c.oid ORDER BY a.attnum"; + + // used when schema defined + var $metaColumnsSQL1 = "SELECT a.attname, t.typname, a.attlen, a.atttypmod, a.attnotnull, a.atthasdef, a.attnum +FROM pg_class c, pg_attribute a, pg_type t, pg_namespace n +WHERE relkind in ('r','v') AND (c.relname='%s' or c.relname = lower('%s')) + and c.relnamespace=n.oid and n.nspname='%s' + and a.attname not like '....%%' AND a.attnum > 0 + AND a.atttypid = t.oid AND a.attrelid = c.oid ORDER BY a.attnum"; + + // get primary key etc -- from Freek Dijkstra + var $metaKeySQL = "SELECT ic.relname AS index_name, a.attname AS column_name,i.indisunique AS unique_key, i.indisprimary AS primary_key + FROM pg_class bc, pg_class ic, pg_index i, pg_attribute a WHERE bc.oid = i.indrelid AND ic.oid = i.indexrelid AND (i.indkey[0] = a.attnum OR i.indkey[1] = a.attnum OR i.indkey[2] = a.attnum OR i.indkey[3] = a.attnum OR i.indkey[4] = a.attnum OR i.indkey[5] = a.attnum OR i.indkey[6] = a.attnum OR i.indkey[7] = a.attnum) AND a.attrelid = bc.oid AND bc.relname = '%s'"; + var $metaDefaultsSQL = "SELECT d.adnum as num, d.adsrc as def from pg_attrdef d, pg_class c where d.adrelid=c.oid and c.relname='%s' order by d.adnum"; + + function MetaError($err=false) + { + include_once(ADODB_DIR."/adodb-error.inc.php"); + if ($err === false) + $err = $this->ErrorNo(); + + return adodb_error($this->dataProvider,$this->databaseType,$err); + } + + function MetaErrorMsg($errno) + { + include_once(ADODB_DIR."/adodb-error.inc.php"); + return adodb_errormsg($errno); + } + + /** + * @returns an array with the primary key columns in it. + */ + function MetaPrimaryKeys($table, $owner=false) + { + // owner not used in base class - see oci8 + $p = array(); + $objs =& $this->MetaColumns($table); + if ($objs) { + foreach($objs as $v) { + if (!empty($v->primary_key)) + $p[] = $v->name; + } + } + if (sizeof($p)) return $p; + if (function_exists('ADODB_VIEW_PRIMARYKEYS')) + return ADODB_VIEW_PRIMARYKEYS($this->databaseType, $this->database, $table, $owner); + return false; + } + + /** + * @returns assoc array where keys are tables, and values are foreign keys + */ + function MetaForeignKeys($table, $owner=false, $upper=false) + { + return false; + } + + // not the fastest implementation - quick and dirty - jlim + // for best performance, use the actual $rs->MetaType(). + function MetaType($t,$len=-1,$fieldobj=false) + { + if (empty($this->_metars)) { + $rsclass = $this->last_module_name . "_ResultSet"; + $this->_metars =& new $rsclass(false,$this->fetchMode); + } + + return $this->_metars->MetaType($t,$len,$fieldobj); + } + + /** + * return the databases that the driver can connect to. + * Some databases will return an empty array. + * + * @return an array of database names. + */ + function MetaDatabases() + { + global $ADODB_FETCH_MODE; + + if ($this->metaDatabasesSQL) { + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + + if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); + + $arr = $this->GetCol($this->metaDatabasesSQL); + if (isset($savem)) $this->SetFetchMode($savem); + $ADODB_FETCH_MODE = $save; + + return $arr; + } + + return false; + } + + /** + * @param ttype can either be 'VIEW' or 'TABLE' or false. + * If false, both views and tables are returned. + * "VIEW" returns only views + * "TABLE" returns only tables + * @param showSchema returns the schema/user with the table name, eg. USER.TABLE + * @param mask is the input mask - only supported by oci8 and postgresql + * + * @return array of tables for current database. + */ + function &MetaTables($ttype=false,$showSchema=false,$mask=false) + { + $info = $this->ServerInfo(); + if ($info['version'] >= 7.3) { + $this->metaTablesSQL = "select tablename,'T' from pg_tables where tablename not like 'pg\_%' + and schemaname not in ( 'pg_catalog','information_schema') + union + select viewname,'V' from pg_views where viewname not like 'pg\_%' and schemaname not in ( 'pg_catalog','information_schema') "; + } + if ($mask) { + $save = $this->metaTablesSQL; + $mask = $this->qstr(strtolower($mask)); + if ($info['version']>=7.3) + $this->metaTablesSQL = " +select tablename,'T' from pg_tables where tablename like $mask and schemaname not in ( 'pg_catalog','information_schema') + union +select viewname,'V' from pg_views where viewname like $mask and schemaname not in ( 'pg_catalog','information_schema') "; + else + $this->metaTablesSQL = " +select tablename,'T' from pg_tables where tablename like $mask + union +select viewname,'V' from pg_views where viewname like $mask"; + } + $ret =& $this->_MetaTables($ttype,$showSchema); + + if ($mask) { + $this->metaTablesSQL = $save; + } + return $ret; + } + + function &_MetaTables($ttype=false,$showSchema=false,$mask=false) + { + global $ADODB_FETCH_MODE; + + $false = false; + if ($mask) { + return $false; + } + if ($this->metaTablesSQL) { + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + + if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); + + $rs = $this->Execute($this->metaTablesSQL); + if (isset($savem)) $this->SetFetchMode($savem); + $ADODB_FETCH_MODE = $save; + + if ($rs === false) return $false; + $arr =& $rs->GetArray(); + $arr2 = array(); + + if ($hast = ($ttype && isset($arr[0][1]))) { + $showt = strncmp($ttype,'T',1); + } + + for ($i=0; $i < sizeof($arr); $i++) { + if ($hast) { + if ($showt == 0) { + if (strncmp($arr[$i][1],'T',1) == 0) $arr2[] = trim($arr[$i][0]); + } else { + if (strncmp($arr[$i][1],'V',1) == 0) $arr2[] = trim($arr[$i][0]); + } + } else + $arr2[] = trim($arr[$i][0]); + } + $rs->Close(); + return $arr2; + } + return $false; + } + + function _findschema(&$table,&$schema) + { + if (!$schema && ($at = strpos($table,'.')) !== false) { + $schema = substr($table,0,$at); + $table = substr($table,$at+1); + } + } + + /** + * List columns in a database as an array of ADOFieldObjects. + * See top of file for definition of object. + * + * @param $table table name to query + * @param $normalize makes table name case-insensitive (required by some databases) + * @schema is optional database schema to use - not supported by all databases. + * + * @return array of ADOFieldObjects for current table. + */ + function &MetaColumns($table,$normalize=true) + { + global $ADODB_FETCH_MODE; + + $schema = false; + $false = false; + $this->_findschema($table,$schema); + + if ($normalize) $table = strtolower($table); + + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); + + if ($schema) $rs =& $this->Execute(sprintf($this->metaColumnsSQL1,$table,$table,$schema)); + else $rs =& $this->Execute(sprintf($this->metaColumnsSQL,$table,$table)); + if (isset($savem)) $this->SetFetchMode($savem); + $ADODB_FETCH_MODE = $save; + + if ($rs === false) { + return $false; + } + if (!empty($this->metaKeySQL)) { + // If we want the primary keys, we have to issue a separate query + // Of course, a modified version of the metaColumnsSQL query using a + // LEFT JOIN would have been much more elegant, but postgres does + // not support OUTER JOINS. So here is the clumsy way. + + $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; + + $rskey = $this->Execute(sprintf($this->metaKeySQL,($table))); + // fetch all result in once for performance. + $keys =& $rskey->GetArray(); + if (isset($savem)) $this->SetFetchMode($savem); + $ADODB_FETCH_MODE = $save; + + $rskey->Close(); + unset($rskey); + } + + $rsdefa = array(); + if (!empty($this->metaDefaultsSQL)) { + $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; + $sql = sprintf($this->metaDefaultsSQL, ($table)); + $rsdef = $this->Execute($sql); + if (isset($savem)) $this->SetFetchMode($savem); + $ADODB_FETCH_MODE = $save; + + if ($rsdef) { + while (!$rsdef->EOF) { + $num = $rsdef->fields['num']; + $s = $rsdef->fields['def']; + if (strpos($s,'::')===false && substr($s, 0, 1) == "'") { /* quoted strings hack... for now... fixme */ + $s = substr($s, 1); + $s = substr($s, 0, strlen($s) - 1); + } + + $rsdefa[$num] = $s; + $rsdef->MoveNext(); + } + } else { + ADOConnection::outp( "==> SQL => " . $sql); + } + unset($rsdef); + } + + $retarr = array(); + while (!$rs->EOF) { + $fld = new ADOFieldObject(); + $fld->name = $rs->fields[0]; + $fld->type = $rs->fields[1]; + $fld->max_length = $rs->fields[2]; + $fld->attnum = $rs->fields[6]; + + if ($fld->max_length <= 0) $fld->max_length = $rs->fields[3]-4; + if ($fld->max_length <= 0) $fld->max_length = -1; + if ($fld->type == 'numeric') { + $fld->scale = $fld->max_length & 0xFFFF; + $fld->max_length >>= 16; + } + // dannym + // 5 hasdefault; 6 num-of-column + $fld->has_default = ($rs->fields[5] == 't'); + if ($fld->has_default) { + $fld->default_value = $rsdefa[$rs->fields[6]]; + } + + //Freek + $fld->not_null = $rs->fields[4] == 't'; + + + // Freek + if (is_array($keys)) { + foreach($keys as $key) { + if ($fld->name == $key['column_name'] AND $key['primary_key'] == 't') + $fld->primary_key = true; + if ($fld->name == $key['column_name'] AND $key['unique_key'] == 't') + $fld->unique = true; // What name is more compatible? + } + } + + if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld; + else $retarr[($normalize) ? strtoupper($fld->name) : $fld->name] = $fld; + + $rs->MoveNext(); + } + $rs->Close(); + if (empty($retarr)) + return $false; + else + return $retarr; + + } + + /** + * List indexes on a table as an array. + * @param table table name to query + * @param primary true to only show primary keys. Not actually used for most databases + * + * @return array of indexes on current table. Each element represents an index, and is itself an associative array. + + Array ( + [name_of_index] => Array + ( + [unique] => true or false + [columns] => Array + ( + [0] => firstname + [1] => lastname + ) + ) + */ + function &MetaIndexes ($table, $primary = FALSE) + { + global $ADODB_FETCH_MODE; + + $schema = false; + $this->_findschema($table,$schema); + + if ($schema) { // requires pgsql 7.3+ - pg_namespace used. + $sql = ' +SELECT c.relname as "Name", i.indisunique as "Unique", i.indkey as "Columns" +FROM pg_catalog.pg_class c +JOIN pg_catalog.pg_index i ON i.indexrelid=c.oid +JOIN pg_catalog.pg_class c2 ON c2.oid=i.indrelid + ,pg_namespace n +WHERE (c2.relname=\'%s\' or c2.relname=lower(\'%s\')) and c.relnamespace=c2.relnamespace and c.relnamespace=n.oid and n.nspname=\'%s\''; + } else { + $sql = ' +SELECT c.relname as "Name", i.indisunique as "Unique", i.indkey as "Columns" +FROM pg_catalog.pg_class c +JOIN pg_catalog.pg_index i ON i.indexrelid=c.oid +JOIN pg_catalog.pg_class c2 ON c2.oid=i.indrelid +WHERE (c2.relname=\'%s\' or c2.relname=lower(\'%s\'))'; + } + + if ($primary == FALSE) { + $sql .= ' AND i.indisprimary=false;'; + } + + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + if ($this->fetchMode !== FALSE) { + $savem = $this->SetFetchMode(FALSE); + } + + $rs = $this->Execute(sprintf($sql,$table,$table,$schema)); + if (isset($savem)) { + $this->SetFetchMode($savem); + } + $ADODB_FETCH_MODE = $save; + + if (!is_object($rs)) { + $false = false; + return $false; + } + + $col_names = $this->MetaColumnNames($table,true,true); + //3rd param is use attnum, + // see http://sourceforge.net/tracker/index.php?func=detail&aid=1451245&group_id=42718&atid=433976 + $indexes = array(); + while ($row = $rs->FetchRow()) { + $columns = array(); + foreach (explode(' ', $row[2]) as $col) { + $columns[] = $col_names[$col]; + } + + $indexes[$row[0]] = array( + 'unique' => ($row[1] == 't'), + 'columns' => $columns + ); + } + return $indexes; + } + + /** + * List columns names in a table as an array. + * @param table table name to query + * + * @return array of column names for current table. + */ + function &MetaColumnNames($table, $numIndexes=false,$useattnum=false /* only for postgres */) + { + $objarr =& $this->MetaColumns($table); + if (!is_array($objarr)) { + $false = false; + return $false; + } + $arr = array(); + if ($numIndexes) { + $i = 0; + if ($useattnum) { + foreach($objarr as $v) + $arr[$v->attnum] = $v->name; + + } else + foreach($objarr as $v) $arr[$i++] = $v->name; + } else + foreach($objarr as $v) $arr[strtoupper($v->name)] = $v->name; + + return $arr; + } + + function MetaTransaction($mode,$db) + { + $mode = strtoupper($mode); + $mode = str_replace('ISOLATION LEVEL ','',$mode); + + switch($mode) { + + case 'READ UNCOMMITTED': + switch($db) { + case 'oci8': + case 'oracle': + return 'ISOLATION LEVEL READ COMMITTED'; + default: + return 'ISOLATION LEVEL READ UNCOMMITTED'; + } + break; + + case 'READ COMMITTED': + return 'ISOLATION LEVEL READ COMMITTED'; + break; + + case 'REPEATABLE READ': + switch($db) { + case 'oci8': + case 'oracle': + return 'ISOLATION LEVEL SERIALIZABLE'; + default: + return 'ISOLATION LEVEL REPEATABLE READ'; + } + break; + + case 'SERIALIZABLE': + return 'ISOLATION LEVEL SERIALIZABLE'; + break; + + default: + return $mode; + } + } + +} + +eval('class postgres64_meta_resultset_EXTENDER extends '. $last_module . '_ResultSet { }'); + +class postgres64_meta_ResultSet extends postgres64_meta_resultset_EXTENDER +{ + /** + * Get the metatype of the column. This is used for formatting. This is because + * many databases use different names for the same type, so we transform the original + * type to our standardised version which uses 1 character codes: + * + * @param t is the type passed in. Normally is ADOFieldObject->type. + * @param len is the maximum length of that field. This is because we treat character + * fields bigger than a certain size as a 'B' (blob). + * @param fieldobj is the field object returned by the database driver. Can hold + * additional info (eg. primary_key for mysql). + * + * @return the general type of the data: + * C for character < 250 chars + * X for teXt (>= 250 chars) + * B for Binary + * N for numeric or floating point + * D for date + * T for timestamp + * L for logical/Boolean + * I for integer + * R for autoincrement counter/integer + * + * + */ + function MetaType($t,$len=-1,$fieldobj=false) + { + if (is_object($t)) { + $fieldobj = $t; + $t = $fieldobj->type; + $len = $fieldobj->max_length; + } + $is_serial = is_object($fieldobj) && $fieldobj->primary_key && $fieldobj->unique && $fieldobj->has_default && substr($fieldobj->default_value,0,8) == 'nextval('; + switch (strtoupper($t)) { + case 'MONEY': // stupid, postgres expects money to be a string + case 'INTERVAL': + case 'CHAR': + case 'CHARACTER': + case 'VARCHAR': + case 'NAME': + case 'BPCHAR': + case '_VARCHAR': + case 'INET': + case 'MACADDR': + if ($len <= $this->blobSize) return 'C'; + case 'TEXT': + return 'X'; + case 'IMAGE': // user defined type + case 'BLOB': // user defined type + case 'BIT': // This is a bit string, not a single bit, so don't return 'L' + case 'VARBIT': + case 'BYTEA': + return 'B'; + case 'BOOL': + case 'BOOLEAN': + return 'L'; + case 'DATE': + return 'D'; + case 'TIMESTAMP WITHOUT TIME ZONE': + case 'TIME': + case 'DATETIME': + case 'TIMESTAMP': + case 'TIMESTAMPTZ': + return 'T'; + case 'INTEGER': return !$is_serial ? 'I' : 'R'; + case 'SMALLINT': + case 'INT2': return !$is_serial ? 'I2' : 'R'; + case 'INT4': return !$is_serial ? 'I4' : 'R'; + case 'BIGINT': + case 'INT8': return !$is_serial ? 'I8' : 'R'; + case 'OID': + case 'SERIAL': + return 'R'; + case 'FLOAT4': + case 'FLOAT8': + case 'DOUBLE PRECISION': + case 'REAL': + return 'F'; + default: + static $typeMap = array( + 'VARCHAR' => 'C', + 'VARCHAR2' => 'C', + 'CHAR' => 'C', + 'C' => 'C', + 'STRING' => 'C', + 'NCHAR' => 'C', + 'NVARCHAR' => 'C', + 'VARYING' => 'C', + 'BPCHAR' => 'C', + 'CHARACTER' => 'C', + 'INTERVAL' => 'C', # Postgres + 'MACADDR' => 'C', # postgres + ## + 'LONGCHAR' => 'X', + 'TEXT' => 'X', + 'NTEXT' => 'X', + 'M' => 'X', + 'X' => 'X', + 'CLOB' => 'X', + 'NCLOB' => 'X', + 'LVARCHAR' => 'X', + ## + 'BLOB' => 'B', + 'IMAGE' => 'B', + 'BINARY' => 'B', + 'VARBINARY' => 'B', + 'LONGBINARY' => 'B', + 'B' => 'B', + ## + 'YEAR' => 'D', // mysql + 'DATE' => 'D', + 'D' => 'D', + ## + 'UNIQUEIDENTIFIER' => 'C', # MS SQL Server + ## + 'TIME' => 'T', + 'TIMESTAMP' => 'T', + 'DATETIME' => 'T', + 'TIMESTAMPTZ' => 'T', + 'T' => 'T', + 'TIMESTAMP WITHOUT TIME ZONE' => 'T', // postgresql + ## + 'BOOL' => 'L', + 'BOOLEAN' => 'L', + 'BIT' => 'L', + 'L' => 'L', + ## + 'COUNTER' => 'R', + 'R' => 'R', + 'SERIAL' => 'R', // ifx + 'INT IDENTITY' => 'R', + ## + 'INT' => 'I', + 'INT2' => 'I', + 'INT4' => 'I', + 'INT8' => 'I', + 'INTEGER' => 'I', + 'INTEGER UNSIGNED' => 'I', + 'SHORT' => 'I', + 'TINYINT' => 'I', + 'SMALLINT' => 'I', + 'I' => 'I', + ## + 'LONG' => 'N', // interbase is numeric, oci8 is blob + 'BIGINT' => 'N', // this is bigger than PHP 32-bit integers + 'DECIMAL' => 'N', + 'DEC' => 'N', + 'REAL' => 'N', + 'DOUBLE' => 'N', + 'DOUBLE PRECISION' => 'N', + 'SMALLFLOAT' => 'N', + 'FLOAT' => 'N', + 'NUMBER' => 'N', + 'NUM' => 'N', + 'NUMERIC' => 'N', + 'MONEY' => 'N', + + ## informix 9.2 + 'SQLINT' => 'I', + 'SQLSERIAL' => 'I', + 'SQLSMINT' => 'I', + 'SQLSMFLOAT' => 'N', + 'SQLFLOAT' => 'N', + 'SQLMONEY' => 'N', + 'SQLDECIMAL' => 'N', + 'SQLDATE' => 'D', + 'SQLVCHAR' => 'C', + 'SQLCHAR' => 'C', + 'SQLDTIME' => 'T', + 'SQLINTERVAL' => 'N', + 'SQLBYTES' => 'B', + 'SQLTEXT' => 'X', + ## informix 10 + "SQLINT8" => 'I8', + "SQLSERIAL8" => 'I8', + "SQLNCHAR" => 'C', + "SQLNVCHAR" => 'C', + "SQLLVARCHAR" => 'X', + "SQLBOOL" => 'L' + ); + + $tmap = false; + $t = strtoupper($t); + $tmap = (isset($typeMap[$t])) ? $typeMap[$t] : 'N'; + if ($t == 'LONG' && $this->dataProvider == 'oci8') return 'B'; + return $tmap; + } + } + +} + +?> \ No newline at end of file diff --git a/lib/adodb/adodbSQL_drivers/postgres64/postgres64_transaction_module.inc b/lib/adodb/adodbSQL_drivers/postgres64/postgres64_transaction_module.inc new file mode 100644 index 0000000..e398fa5 --- /dev/null +++ b/lib/adodb/adodbSQL_drivers/postgres64/postgres64_transaction_module.inc @@ -0,0 +1,128 @@ +transOff > 0) { + $this->transOff += 1; + return; + } + $this->transaction_status = true; + + if ($this->debug && $this->transCnt > 0) + ADOConnection::outp("Bad Transaction: StartTrans called within BeginTrans"); + + $this->BeginTrans(); + $this->transOff = 1; + } + + function BeginTrans() + { + if ($this->transOff) + return true; + + $this->transCnt += 1; + return @pg_Exec($this->connectionId, "begin"); + } + + function CompleteTrans($autoComplete = true) + { + if ($this->transOff > 1) { + $this->transOff -= 1; + return true; + } + $this->transOff = 0; + if ($this->transaction_status && $autoComplete) { + if (!$this->CommitTrans()) { + $this->transaction_status = false; + if ($this->debug) + ADOConnection::outp("Smart Commit failed"); + } else + if ($this->debug) + ADOConnection::outp("Smart Commit occurred"); + } else { + $this->RollbackTrans(); + if ($this->debug) + ADOCOnnection::outp("Smart Rollback occurred"); + } + return $this->transaction_status; + } + + function CommitTrans($ok=true) + { + if ($this->transOff) + return true; + + if (!$ok) + return $this->RollbackTrans(); + + $this->transCnt -= 1; + return @pg_Exec($this->connectionId, "commit"); + } + + function RollbackTrans() + { + if ($this->transOff) + return true; + + $this->transCnt -= 1; + return @pg_Exec($this->connectionId, "rollback"); + } + + function FailTrans() + { + if ($this->debug) + if ($this->transOff == 0) { + ADOConnection::outp("FailTrans outside StartTrans/CompleteTrans"); + } else { + ADOConnection::outp("FailTrans was called"); + } + $this->transaction_status = false; + } + + function HasFailedTrans() + { + if ($this->transOff > 0) + return $this->transaction_status == false; + + return false; + } + + function RowLock($tables,$where,$flds='1 as ignore') + { + if (!$this->transCnt) + $this->BeginTrans(); + + return $this->GetOne("select $flds from $tables where $where for update"); + } + + function CommitLock($table) + { + return $this->CommitTrans(); + } + + function RollbackLock($table) + { + return $this->RollbackTrans(); + } + +} + +eval('class postgres64_transaction_resultset_EXTENDER extends '. $last_module . '_ResultSet { }'); + +class postgres64_transaction_ResultSet extends postgres64_transaction_resultset_EXTENDER +{ +} +?> \ No newline at end of file diff --git a/lib/adodb/adodbSQL_drivers/postgres7/postgres7_datadict.inc b/lib/adodb/adodbSQL_drivers/postgres7/postgres7_datadict.inc new file mode 100644 index 0000000..1f8888d --- /dev/null +++ b/lib/adodb/adodbSQL_drivers/postgres7/postgres7_datadict.inc @@ -0,0 +1,211 @@ +TableName ($tabname); + $sql = array(); + list($lines,$pkey) = $this->_GenFields($flds); + $alter = 'ALTER TABLE ' . $tabname . $this->addCol . ' '; + foreach($lines as $v) { + if (($not_null = preg_match('/NOT NULL/i',$v))) { + $v = preg_replace('/NOT NULL/i','',$v); + } + if (preg_match('/^([^ ]+) .*(DEFAULT [^ ]+)/',$v,$matches)) { + list(,$colname,$default) = $matches; + $sql[] = $alter . str_replace($default,'',$v); + $sql[] = 'ALTER TABLE '.$tabname.' ALTER COLUMN '.$colname.' SET ' . $default; + } else { + $sql[] = $alter . $v; + } + if ($not_null) { + list($colname) = explode(' ',$v); + $sql[] = 'ALTER TABLE '.$tabname.' ALTER COLUMN '.$colname.' SET NOT NULL'; + } + } + return $sql; + } + + function AlterColumnSQL($tabname, $flds, $tableflds='',$tableoptions='') + { + if (!$tableflds) { + if ($this->debug) $this->outp("AlterColumnSQL needs a complete table-definiton for PostgreSQL"); + return array(); + } + return $this->_recreate_copy_table($tabname,False,$tableflds,$tableoptions); + } + + function DropColumnSQL($tabname, $flds, $tableflds='',$tableoptions='') + { + $has_drop_column = 7.3 <= (float) @$this->serverInfo['version']; + if (!$has_drop_column && !$tableflds) { + if ($this->debug) $this->outp("DropColumnSQL needs complete table-definiton for PostgreSQL < 7.3"); + return array(); + } + if ($has_drop_column) { + return ADODB_DataDict::DropColumnSQL($tabname, $flds); + } + return $this->_recreate_copy_table($tabname,$flds,$tableflds,$tableoptions); + } + + function DropTableSQL($tabname) + { + $sql = ADODB_DataDict::DropTableSQL($tabname); + $drop_seq = $this->_DropAutoIncrement($tabname); + if ($drop_seq) $sql[] = $drop_seq; + return $sql; + } + + function _CreateSuffix($fname, &$ftype, $fnotnull,$fdefault,$fautoinc,$fconstraint) + { + if ($fautoinc) { + $ftype = 'SERIAL'; + return ''; + } + $suffix = ''; + if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault"; + if ($fnotnull) $suffix .= ' NOT NULL'; + if ($fconstraint) $suffix .= ' '.$fconstraint; + return $suffix; + } + + function _DropAutoIncrement($tabname) + { + $tabname = $this->connection->qstr('%'.$tabname.'%'); + + $seq = $this->connection->GetOne("SELECT relname FROM pg_class WHERE NOT relname ~ 'pg_.*' AND relname LIKE $tabname AND relkind='S'"); + + // check if a tables depends on the sequenz and it therefor cant and dont need to be droped separatly + if (!$seq || $this->connection->GetOne("SELECT relname FROM pg_class JOIN pg_depend ON pg_class.relfilenode=pg_depend.objid WHERE relname='$seq' AND relkind='S' AND deptype='i'")) { + return False; + } + return "DROP SEQUENCE ".$seq; + } + + function _IndexSQL($idxname, $tabname, $flds, $idxoptions) + { + $sql = array(); + if ( isset($idxoptions['REPLACE']) || isset($idxoptions['DROP']) ) { + $sql[] = sprintf ($this->dropIndex, $idxname, $tabname); + if ( isset($idxoptions['DROP']) ) + return $sql; + } + if ( empty ($flds) ) { + return $sql; + } + + $unique = isset($idxoptions['UNIQUE']) ? ' UNIQUE' : ''; + $s = 'CREATE' . $unique . ' INDEX ' . $idxname . ' ON ' . $tabname . ' '; + if (isset($idxoptions['HASH'])) + $s .= 'USING HASH '; + + if ( isset($idxoptions[$this->upperName]) ) + $s .= $idxoptions[$this->upperName]; + + if ( is_array($flds) ) + $flds = implode(', ',$flds); + $s .= '(' . $flds . ')'; + $sql[] = $s; + return $sql; + } + + + function _recreate_copy_table($tabname,$dropflds,$tableflds,$tableoptions='') + { + if ($dropflds && !is_array($dropflds)) $dropflds = explode(',',$dropflds); + $copyflds = array(); + foreach($this->MetaColumns($tabname) as $fld) { + if (!$dropflds || !in_array($fld->name,$dropflds)) { + // we need to explicit convert varchar to a number to be able to do an AlterColumn of a char column to a nummeric one + if (preg_match('/'.$fld->name.' (I|I2|I4|I8|N|F)/i',$tableflds,$matches) && + in_array($fld->type,array('varchar','char','text','bytea'))) { + $copyflds[] = "to_number($fld->name,'S99D99')"; + } else { + $copyflds[] = $fld->name; + } + // identify the sequence name and the fld its on + if ($fld->primary_key && $fld->has_default && + preg_match("/nextval\('([^']+)'::text\)/",$fld->default_value,$matches)) { + $seq_name = $matches[1]; + $seq_fld = $fld->name; + } + } + } + $copyflds = implode(', ',$copyflds); + + $tempname = $tabname.'_tmp'; + $aSql[] = 'BEGIN'; // we use a transaction, to make sure not to loose the content of the table + $aSql[] = "SELECT * INTO TEMPORARY TABLE $tempname FROM $tabname"; + $aSql = array_merge($aSql,$this->DropTableSQL($tabname)); + $aSql = array_merge($aSql,$this->CreateTableSQL($tabname,$tableflds,$tableoptions)); + $aSql[] = "INSERT INTO $tabname SELECT $copyflds FROM $tempname"; + if ($seq_name && $seq_fld) { // if we have a sequence we need to set it again + $seq_name = $tabname.'_'.$seq_fld.'_seq'; // has to be the name of the new implicit sequence + $aSql[] = "SELECT setval('$seq_name',MAX($seq_fld)) FROM $tabname"; + } + $aSql[] = "DROP TABLE $tempname"; + // recreate the indexes, if they not contain one of the droped columns + foreach($this->MetaIndexes($tabname) as $idx_name => $idx_data) + { + if (substr($idx_name,-5) != '_pkey' && (!$dropflds || !count(array_intersect($dropflds,$idx_data['columns'])))) { + $aSql = array_merge($aSql,$this->CreateIndexSQL($idx_name,$tabname,$idx_data['columns'], + $idx_data['unique'] ? array('UNIQUE') : False)); + } + } + $aSql[] = 'COMMIT'; + return $aSql; + } +} + +?> \ No newline at end of file diff --git a/lib/adodb/adodbSQL_drivers/postgres7/postgres7_date_module.inc b/lib/adodb/adodbSQL_drivers/postgres7/postgres7_date_module.inc new file mode 100644 index 0000000..06eea17 --- /dev/null +++ b/lib/adodb/adodbSQL_drivers/postgres7/postgres7_date_module.inc @@ -0,0 +1,316 @@ +_Execute("select $this->sysTimeStamp"); + if ($rs && !$rs->EOF) + return $this->UnixTimeStamp(reset($rs->fields)); + + return false; + } + + function OffsetDate($dayFraction, $date=false) + { + if (!$date) + $date = $this->sysDate; + else if (strncmp($date,"'",1) == 0) { + $len = strlen($date); + if (10 <= $len && $len <= 12) + $date = 'date ' . $date; + else $date = 'timestamp ' . $date; + } + return "($date+interval'$dayFraction days')"; + } + + function SetDateLocale($locale = 'En') + { + $this->locale = $locale; + switch (strtoupper($locale)) + { + case 'EN': + $this->fmtDate="'Y-m-d'"; + $this->fmtTimeStamp = "'Y-m-d H:i:s'"; + break; + + case 'US': + $this->fmtDate = "'m-d-Y'"; + $this->fmtTimeStamp = "'m-d-Y H:i:s'"; + break; + + case 'NL': + case 'FR': + case 'RO': + case 'IT': + $this->fmtDate="'d-m-Y'"; + $this->fmtTimeStamp = "'d-m-Y H:i:s'"; + break; + + case 'GE': + $this->fmtDate="'d.m.Y'"; + $this->fmtTimeStamp = "'d.m.Y H:i:s'"; + break; + + default: + $this->fmtDate="'Y-m-d'"; + $this->fmtTimeStamp = "'Y-m-d H:i:s'"; + break; + } + } + + function DBDate($date) + { + if (empty($date) && $date !== 0) + return 'null'; + + if (is_string($date) && !is_numeric($date)) { + if ($date === 'null' || strncmp($date, "'", 1) === 0) + return $date; + + if ($this->isoDates) + return "'$date'"; + + $date = $this->UnixDate($date); + } + + return adodb_date($this->fmtDate,$date); + } + + function DBTimeStamp($timestamp) + { + if (empty($timestamp) && $timestamp !== 0) + return 'null'; + + # strlen(14) allows YYYYMMDDHHMMSS format + if (!is_string($timestamp) || (is_numeric($timestamp) && strlen($timestamp)<14)) + return adodb_date($this->fmtTimeStamp, $timestamp); + + if ($timestamp === 'null') + return $timestamp; + + if ($this->isoDates && strlen($timestamp) !== 14) + return "'$timestamp'"; + + $timestamp = $this->UnixTimeStamp($timestamp); + return adodb_date($this->fmtTimeStamp, $timestamp); + } + + function UnixDate($v) + { + if (is_object($v)) { + // odbtp support + //( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 ) + return adodb_mktime($v->hour, $v->minute, $v->second, $v->month, $v->day, $v->year); + } + + if (is_numeric($v) && strlen($v) !== 8) + return $v; + + if (!preg_match( "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})|", ($v), $rr)) + return false; + + if ($rr[1] <= TIMESTAMP_FIRST_YEAR) + return 0; + + // h-m-s-MM-DD-YY + return adodb_mktime(0, 0, 0, $rr[2], $rr[3], $rr[1]); + } + + function UnixTimeStamp($v) + { + if (is_object($v)) { + // odbtp support + //( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 ) + return adodb_mktime($v->hour, $v->minute, $v->second, $v->month, $v->day, $v->year); + } + + if (!preg_match("|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ ,-]*(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,4}))?|", ($v), $rr)) + return false; + + if ($rr[1] <= TIMESTAMP_FIRST_YEAR && $rr[2]<= 1) + return 0; + + // h-m-s-MM-DD-YY + if (!isset($rr[5])) + return adodb_mktime(0, 0, 0, $rr[2], $rr[3], $rr[1]); + else return adodb_mktime($rr[5], $rr[6], $rr[7], $rr[2], $rr[3], $rr[1]); + } + + function UserDate($v, $fmt='Y-m-d', $gmt=false) + { + $tt = $this->UnixDate($v); + + // $tt == -1 if pre TIMESTAMP_FIRST_YEAR + if (($tt === false || $tt == -1) && $v != false) + return $v; + else if ($tt == 0) + return $this->emptyDate; + else if ($tt == -1) { // pre-TIMESTAMP_FIRST_YEAR + } + + return ($gmt) ? adodb_gmdate($fmt, $tt) : adodb_date($fmt, $tt); + } + + function UserTimeStamp($v, $fmt='Y-m-d H:i:s', $gmt=false) + { + if (!isset($v)) + return $this->emptyTimeStamp; + + # strlen(14) allows YYYYMMDDHHMMSS format + if (is_numeric($v) && strlen($v)<14) + return ($gmt) ? adodb_gmdate($fmt,$v) : adodb_date($fmt,$v); + + $tt = $this->UnixTimeStamp($v); + // $tt == -1 if pre TIMESTAMP_FIRST_YEAR + if (($tt === false || $tt == -1) && $v != false) + return $v; + + if ($tt == 0) + return $this->emptyTimeStamp; + else return ($gmt) ? adodb_gmdate($fmt,$tt) : adodb_date($fmt,$tt); + } + + function SQLDate($fmt, $col=false) + { + if (!$col) + $col = $this->sysTimeStamp; + + $s = 'TO_CHAR('.$col.",'"; + $len = strlen($fmt); + for ($i=0; $i < $len; $i++) { + $ch = $fmt[$i]; + switch($ch) { + case 'Y': + case 'y': + $s .= 'YYYY'; + break; + + case 'Q': + case 'q': + $s .= 'Q'; + break; + + case 'M': + $s .= 'Mon'; + break; + + case 'm': + $s .= 'MM'; + break; + + case 'D': + case 'd': + $s .= 'DD'; + break; + + case 'H': + $s.= 'HH24'; + break; + + case 'h': + $s .= 'HH'; + break; + + case 'i': + $s .= 'MI'; + break; + + case 's': + $s .= 'SS'; + break; + + case 'a': + case 'A': + $s .= 'AM'; + break; + + case 'w': + $s .= 'D'; + break; + + case 'l': + $s .= 'DAY'; + break; + + default: + // handle escape characters... + if ($ch == '\\') { + $i++; + $ch = substr($fmt,$i,1); + } + if (strpos('-/.:;, ',$ch) !== false) + $s .= $ch; + else $s .= '"'.$ch.'"'; + } + } + return $s. "')"; + } +} + +eval('class postgres7_date_resultset_EXTENDER extends '. $last_module . '_ResultSet { }'); + +class postgres7_date_ResultSet extends postgres7_date_resultset_EXTENDER +{ + var $emptyTimeStamp = ' '; /// what to display when $time==0 + var $emptyDate = ' '; /// what to display when $time==0 + var $datetime = false; + + function UserTimeStamp($v, $fmt='Y-m-d H:i:s') + { + if (is_numeric($v) && strlen($v)<14) + return adodb_date($fmt,$v); + + $tt = $this->UnixTimeStamp($v); + // $tt == -1 if pre TIMESTAMP_FIRST_YEAR + if (($tt === false || $tt == -1) && $v != false) + return $v; + + if ($tt === 0) + return $this->emptyTimeStamp; + else return adodb_date($fmt,$tt); + } + + function UserDate($v,$fmt='Y-m-d') + { + $tt = $this->UnixDate($v); + // $tt == -1 if pre TIMESTAMP_FIRST_YEAR + if (($tt === false || $tt == -1) && $v != false) + return $v; + else if ($tt == 0) + return $this->emptyDate; + else if ($tt == -1) { // pre-TIMESTAMP_FIRST_YEAR + } + return adodb_date($fmt,$tt); + } + + function UnixDate($v) + { + return postgres7_date_ADOConnection::UnixDate($v); + } + + function UnixTimeStamp($v) + { + return postgres7_date_ADOConnection::UnixTimeStamp($v); + } + +} +?> \ No newline at end of file diff --git a/lib/adodb/adodbSQL_drivers/postgres7/postgres7_driver.inc b/lib/adodb/adodbSQL_drivers/postgres7/postgres7_driver.inc new file mode 100644 index 0000000..50de7fa --- /dev/null +++ b/lib/adodb/adodbSQL_drivers/postgres7/postgres7_driver.inc @@ -0,0 +1,680 @@ +dbtype = 'postgres7'; + $this->dataProvider = 'postgres'; + } + + /** + * Connection to database server and selected database + * + * @access private + */ + + function _connect($host = "", $username = "", $password = "", $database = "", $persistent, $forcenew) + { + if (!function_exists('pg_connect')) return false; + + $this->host = $host; + $this->username = $this->query_addslashes($username); + $this->password = $this->query_addslashes($password); + if (strlen($database) == 0) $database = 'template1'; + $this->database = $this->query_addslashes($database); + + if ($this->username || $this->password || $this->database) { + $this->connect_string = $this->host; + if ($this->connect_string) { + $host = split(":", $this->connect_string); + if ($host[0]) $this->connect_string = "host=" . $this->query_addslashes($host[0]); + else $this->connect_string = 'host=localhost'; + if (isset($host[1])) $this->connect_string .= " port=$host[1]"; + else if (!empty($this->port)) $this->connect_string .= " port=" . $this->port; + } + if ($this->username) $this->connect_string .= " user=" . $this->username; + if ($this->password) $this->connect_string .= " password=" . $this->password; + if ($this->database) $this->connect_string .= " dbname=" . $this->database; + } + else + { + $this->connect_string = $this->host; + } + + $this->persistent = $persistent; + $this->forcenewconnection = $forcenew; + + $this->_makeconnection(); + + if ($this->connectionId === false) + { + if ($fn = $this->raiseErrorFn) + $fn($this->dbtype, 'CONNECT', $this->ErrorNo(), $this->ErrorMsg(), $this->host, $this->database, $this); + return $this->SelectDB( $this->database ); + } + + return true; + } + + function _makeconnection() + { + if($this->persistent == 1) + { + $this->connectionId = @pg_pconnect( $this->connect_string ); + } + else + { + if(!$this->forcenewconnection) + { + $this->connectionId = @pg_connect( $this->connect_string ); + } + else + { + $this->connectionId = @pg_connect( $this->connect_string, PGSQL_CONNECT_FORCE_NEW ); + } + } + } + + function query_addslashes($query) + { + $len = strlen($query); + if ($len == 0) + return "''"; + if (strncmp($query,"'",1) === 0 && substr($query,$len-1) == "'") + return $s; + return "'".addslashes($query)."'"; + } + + /** + * Choose a database to connect. + * + * @param dbname is the name of the database to select + * @return true or false + * @access public + */ + + function SelectDB($dbname) + { + $this->database = $dbname; + + if ($this->connectionId === false) + { + if($this->createdatabase == true) + { + $this->connectionId = @pg_pconnect( "host=$this->host user=$this->username password=$this->password" ); + $result = @pg_query($this->connectionId, "CREATE DATABASE " . $this->database ); + if ($result === false) { // error handling if query fails + $this->connectionId = false; + return false; + } + + $this->_makeconnection(); + if($this->connectionId === false) + { + $this->connectionId = false; + return false; + } + else + { + return true; + } + } + $this->connectionId = false; + return false; + } + else + { + return true; + } + } + + /** + * Return database error message + * Usage: $errormessage =& $db->ErrorMsg(); + * + * @access public + */ + + function ErrorMsg() + { + return @pg_last_error($this->connectionId); + } + + /** + * Return database error number + * Usage: $errorbo =& $db->ErrorNo(); + * + * @access public + */ + + function ErrorNo() + { + $error = @pg_last_error( $this->connectionId ); + return strlen($error) ? $error : 0; + } + + /** + * Returns # of affected rows from insert/delete/update query + * + * @access public + * @return integer Affected rows + */ + + function Affected_Rows() + { + return @pg_affected_rows( $this->record_set ); + } + + /** + * Returns the last record id of an inserted item + * Usage: $db->Insert_ID(); + * + * @access public + */ + + function Insert_ID() + { + return @pg_getlastoid($this->record_set); + } + + /** + * Correctly quotes a string so that all strings are escape coded. + * An example is $db->qstr("Haven't a clue."); + * + * @param string the string to quote + * @param [magic_quotes] if $s is GET/POST var, set to get_magic_quotes_gpc(). + * + * @return single-quoted string IE: 'Haven\'t a clue.' + */ + + function qstr($string, $magic_quotes=false) + { + // Ugly hack alert! Repalce backticks with some arbitrary character + $string = str_replace('`', "\x1C", $string); + if (!$magic_quotes) { + if (strnatcmp(PHP_VERSION, '4.2.0') >= 0 && $this->pg_escape_enable) { + return "'" . pg_escape_string($string) . "'"; + } + $string = str_replace("'", "\\'", str_replace('\\', '\\\\', str_replace("\0", "\\\0", $string))); + return "'" . $string . "'"; + } + return "'" . str_replace('\\"', '"', $string) . "'"; + } + + function QMagic($string) + { + return $this->qstr($string, get_magic_quotes_gpc()); + } + + /** + * Returns concatenated string + * Usage: $db->Concat($str1,$str2); + * + * @return concatenated string + */ + function Concat() + { + $arr = func_get_args(); + return implode("||", $arr); + } + + function IfNull( $field, $ifNull ) + { + return " coalesce($field, $ifNull) "; + } + + /** + * Closes database connection + * Usage: $db->close(); + * + * @access public + */ + + function Close() + { + @pg_close( $this->connectionId ); + $this->connectionId = false; + } + + /** + * Returns All Records in an array + * + * Usage: $db->GetAll($sql); + * @access public + */ + + function &GetAll($sql, $inputarr = false) + { + $data =& $this->GetArray($sql, $inputarr); + return $data; + } + + /** + * Returns All Records in an array + * + * Usage: $db->GetArray($sql); + * @access public + */ + + function &GetArray($sql, $inputarr = false) + { + $data = false; + $result =& $this->Execute($sql, $inputarr); + if ($result) + { + $data =& $result->GetArray(); + $result->Close(); + } + return $data; + } + + /** + * Executes SQL query and instantiates resultset methods + * + * @access private + * @return mixed Resultset methods + */ + + function &do_query( $sql, $offset, $nrows, $inputarr=false ) + { + global $ADODB_FETCH_MODE; + $sql = str_replace('`', '"', $sql); + $sql = str_replace("\x1C", "`", $sql); + $sql = str_replace('IS_DELETED', 'is_deleted', $sql); + $sql = str_replace("HIGH_PRIORITY", "", $sql); + $false = false; + + $offsetStr = ''; + $limitStr = ''; + if ($offset != -1 || $nrows != -1) + { + $offsetStr = ($offset >= 0) ? " OFFSET " . ((integer)$offset) : ''; + $limitStr = ($nrows >= 0) ? " LIMIT " . ((integer)$nrows) : ''; + } + + if ($inputarr && is_array($inputarr)) { + $sqlarr = explode('?', $sql); + if (!is_array(reset($inputarr))) $inputarr = array($inputarr); + foreach($inputarr as $arr) { + $sql = ''; $i = 0; + foreach($arr as $v) { + $sql .= $sqlarr[$i]; + switch(gettype($v)){ + case 'string': + $sql .= $this->qstr($v); + break; + case 'double': + $sql .= str_replace(',', '.', $v); + break; + case 'boolean': + $sql .= $v ? "'t'" : "'f'"; + break; + default: + if ($v === null) + $sql .= 'NULL'; + else $sql .= $v; + } + $i += 1; + } + $sql .= $sqlarr[$i]; + if ($i+1 != sizeof($sqlarr)) + return $false; + $this->sql = $sql . "$limitStr$offsetStr"; + $time_start = array_sum(explode(' ', microtime())); + $this->query_count++; + $resultId = @pg_query( $this->sql); + $time_total = (array_sum(explode(' ', microtime())) - $time_start); + $this->query_time_total += $time_total; + if($this->debug_console) + { + $this->query_list[] = $this->sql; + $this->query_list_time[] = $time_total; + $this->query_list_errors[] = $this->ErrorMsg(); + } + if($this->debug) + { + $this->outp($sql . "$limitStr$offsetStr"); + } + if ($resultId === false) { // error handling if query fails + if ($fn = $this->raiseErrorFn) + $fn($this->dbtype, 'EXECUTE', $this->ErrorNo(), $this->ErrorMsg(), $this->sql, $inputarr, $this); + return $false; + } + } + } + else + { + $this->sql = $sql . "$limitStr$offsetStr"; + $time_start = array_sum(explode(' ', microtime())); + $this->query_count++; + $resultId = @pg_query( $this->sql); + $time_total = (array_sum(explode(' ', microtime())) - $time_start); + $this->query_time_total += $time_total; + if($this->debug_console) + { + $this->query_list[] = $this->sql; + $this->query_list_time[] = $time_total; + $this->query_list_errors[] = $this->ErrorMsg(); + } + if($this->debug) + { + $this->outp($sql . "$limitStr$offsetStr"); + } + } + + if ($resultId === false) { // error handling if query fails + if ($fn = $this->raiseErrorFn) + $fn($this->dbtype, 'EXECUTE', $this->ErrorNo(), $this->ErrorMsg(), $this->sql, $inputarr, $this); + return $false; + } + + if (@pg_numfields( $resultId ) <= 0) { // return simplified recordset for inserts/updates/deletes with lower overhead + $recordset = new ADORecordSet_empty(); + $this->record_set = $recordset; + return $recordset; + } + + $resultset_name = $this->last_module_name . "_ResultSet"; + $recordset = new $resultset_name( $resultId, $this->connectionId ); + $this->record_set = $recordset; + + $recordset->_currentRow = 0; + + switch ($ADODB_FETCH_MODE) + { + case ADODB_FETCH_NUM: $recordset->fetchMode = PGSQL_NUM; break; + case ADODB_FETCH_ASSOC:$recordset->fetchMode = PGSQL_ASSOC; break; + default: + case ADODB_FETCH_DEFAULT: + case ADODB_FETCH_BOTH:$recordset->fetchMode = PGSQL_BOTH; break; + } + + $recordset->_numOfRows = @pg_numrows( $resultId ); + if( $recordset->_numOfRows == 0) + { + $recordset->EOF = true; + } + $recordset->_numOfFields = @pg_numfields( $resultId ); + $recordset->_fetch(); + + return $recordset; + } +} + +class postgres7_driver_ResultSet +{ + var $connectionId; + var $fields; + var $resultId; + var $_currentRow = 0; + var $_numOfRows = -1; + var $_numOfFields = -1; + var $fetchMode; + var $EOF; + + /** + * pgsqlResultSet Constructor + * + * @access private + * @param string $record + * @param string $resultId + */ + + function postgres7_driver_ResultSet( $resultId, $connectionId ) + { + $this->fields = array(); + $this->connectionId = $connectionId; + $this->record = array(); + $this->resultId = $resultId; + $this->EOF = false; + } + + /** + * Frees resultset + * + * @access public + */ + + function Close() + { + pg_free_result( $this->resultId ); + $this->fields = array(); + $this->resultId = false; + } + + /** + * Returns field name from select query + * + * @access public + * @param string $field + * @return string Field name + */ + + function fields( $field ) + { + if(empty($field)) + { + return $this->fields; + } + else + { + return $this->fields[$field]; + } + } + + /** + * Returns numrows from select query + * + * @access public + * @return integer Numrows + */ + + function RecordCount() + { + return $this->_numOfRows; + } + + /** + * PEAR DB Compatable Command + */ + function NumRows() + { + return $this->_numOfRows; + } + + /** + * Returns num of fields from select query + * + * @access public + * @return integer numfields + */ + + function FieldCount() + { + return $this->_numOfFields; + } + + /** + * PEAR DB Compatable Command + */ + function NumCols() + { + return $this->_numOfFields; + } + + /** + * Returns next record + * + * @access public + */ + + function MoveNext() + { + if (@$this->fields = pg_fetch_array($this->resultId, NULL, $this->fetchMode)) { + $this->_currentRow += 1; + return true; + } + if (!$this->EOF) { + $this->_currentRow += 1; + $this->EOF = true; + } + return false; + } + + /** + * Move to the first row in the recordset. Many databases do NOT support this. + * + * @return true or false + */ + + function MoveFirst() + { + if ($this->_currentRow == 0) return true; + return $this->Move(0); + } + + /** + * Returns the Last Record + * + * @access public + */ + + function MoveLast() + { + if ($this->EOF) return false; + return $this->Move($this->_numOfRows - 1); + } + + /** + * Random access to a specific row in the recordset. Some databases do not support + * access to previous rows in the databases (no scrolling backwards). + * + * @param rowNumber is the row to move to (0-based) + * + * @return true if there still rows available, or false if there are no more rows (EOF). + */ + + function Move($rowNumber = 0) + { + if ($rowNumber == $this->_currentRow) return true; + $this->EOF = false; + if ($this->_numOfRows > 0){ + if ($rowNumber >= $this->_numOfRows - 1){ + $rowNumber = $this->_numOfRows - 1; + } + } + + if ($this->_seek($rowNumber)) { + $this->_currentRow = $rowNumber; + if ($this->_fetch()) { + return true; + } + $this->fields = false; + } + $this->EOF = true; + return false; + } + + /** + * Perform Seek to specific row + * + * @access private + */ + + function _seek($row) + { + if ($this->_numOfRows == 0) return false; + return @pg_result_seek($this->resultId,$row); + } + + /** + * Fills field array with first database element when query initially executed + * + * @access private + */ + + function _fetch() + { + $this->fields = @pg_fetch_array($this->resultId, NULL, $this->fetchMode); + return is_array($this->fields); + } + + /** + * Check to see if last record reached + * + * @access public + */ + + function EOF() + { + if( $this->_currentRow < $this->_numOfRows) + { + return false; + } + else + { + $this->EOF = true; + return true; + } + } + + /** + * Returns All Records in an array + * + * @access public + * @param [nRows] is the number of rows to return. -1 means every row. + */ + + function &GetArray($nRows = -1) + { + $results = array(); + $cnt = 0; + while (!$this->EOF && $nRows != $cnt) { + $results[] = $this->fields; + $this->MoveNext(); + $cnt++; + } + return $results; + } + + function &GetRows($nRows = -1) + { + $arr =& $this->GetArray($nRows); + return $arr; + } + + function &GetAll($nRows = -1) + { + $arr =& $this->GetArray($nRows); + return $arr; + } + + /** + * Fetch field information for a table. + * + * @return object containing the name, type and max_length + */ + function FetchField($fieldOffset = -1) + { + $fieldObject= new ADOFieldObject(); + $fieldObject->name = @pg_fieldname($this->resultId, $fieldOffset); + $fieldObject->type = @pg_fieldtype($this->resultId, $fieldOffset); + $fieldObject->max_length = @pg_fieldsize($this->resultId, $fieldOffset); + return $fieldObject; + } +} +?> \ No newline at end of file diff --git a/lib/adodb/adodbSQL_drivers/postgres7/postgres7_extend_module.inc b/lib/adodb/adodbSQL_drivers/postgres7/postgres7_extend_module.inc new file mode 100644 index 0000000..68b16ec --- /dev/null +++ b/lib/adodb/adodbSQL_drivers/postgres7/postgres7_extend_module.inc @@ -0,0 +1,147 @@ +Execute($sql, $inputarr); + if ($result) { + $data =& $result->GetAssoc($force_array, $first2cols); + $result->Close(); + } + return $data; + } + + /** + * Generates a sequence id and stores it in $this->genID; + * GenID is only available if $this->hasGenID = true; + * + * @param seqname name of sequence to use + * @param startID if sequence does not exist, start at this ID + * @return 0 if not supported, otherwise a sequence id + */ + + var $_genIDSQL = "SELECT NEXTVAL('%s')"; + var $_genSeqSQL = "CREATE SEQUENCE %s START %s"; + var $_dropSeqSQL = "DROP SEQUENCE %s"; + var $genID = 0; + + function GenID($seqname='adodbseq', $startID=1) + { + $getnext = sprintf($this->_genIDSQL, $seqname); + $holdtransOK = $this->transaction_status; + $save_handler = $this->raiseErrorFn; + $this->raiseErrorFn = ''; + @($result = $this->Execute($getnext)); + $this->raiseErrorFn = $save_handler; + + if (!$result) { + $this->transaction_status = $holdtransOK; + $createseq = $this->Execute(sprintf($this->_genSeqSQL, $seqname, $startID)); + $result = $this->Execute($getnext); + } + if ($result && !$result->EOF) + $this->genID = reset($result->fields); + else $this->genID = 0; + + if ($result) + $result->Close(); + + return $this->genID; + } + + function CreateSequence($seqname='adodbseq', $startID=1) + { + return $this->Execute(sprintf($this->_genSeqSQL, $seqname, $startID)); + } + + function DropSequence($seqname='adodbseq') + { + return $this->Execute(sprintf($this->_dropSeqSQL, $seqname)); + } + +} + +eval('class postgres7_extend_resultset_EXTENDER extends '. $last_module . '_ResultSet { }'); + +class postgres7_extend_ResultSet extends postgres7_extend_resultset_EXTENDER +{ + function &GetAssoc($force_array = false, $first2cols = false) + { + $results = false; + + if ($this->_numOfFields > 1) { + $numIndex = isset($this->fields[0]); + $results = array(); + if (!$first2cols && ($this->_numOfFields > 2 || $force_array)) { + if ($numIndex) { + while (!$this->EOF) { + $results[trim($this->fields[0])] = array_slice($this->fields, 1); + $this->MoveNext(); + } + } else { + while (!$this->EOF) { + $results[trim(reset($this->fields))] = array_slice($this->fields, 1); + $this->MoveNext(); + } + } + } else { + if ($numIndex) { + while (!$this->EOF) { + $results[trim(($this->fields[0]))] = $this->fields[1]; + $this->MoveNext(); + } + } else { + while (!$this->EOF) { + $v1 = trim(reset($this->fields)); + $v2 = ''.next($this->fields); + $results[$v1] = $v2; + $this->MoveNext(); + } + } + } + } + return $results; + } + + function PO_RecordCount($table="", $condition="") + { + $lnumrows = $this->_numOfRows; + if($lnumrows == -1 && $this->connectionId) + { + if($table) + { + if ($condition) + $condition = " WHERE " . $condition; + $resultrows = &$this->connectionId->Execute("SELECT COUNT(*) FROM $table $condition"); + if ($resultrows) + $lnumrows = reset($resultrows->fields); + } + } + return $lnumrows; + } + + function CurrentRow() + { + return $this->_currentRow; + } + + function AbsolutePosition() + { + return $this->_currentRow; + } + + function NextRecordSet() + { + return false; + } + +} +?> \ No newline at end of file diff --git a/lib/adodb/adodbSQL_drivers/postgres7/postgres7_meta_module.inc b/lib/adodb/adodbSQL_drivers/postgres7/postgres7_meta_module.inc new file mode 100644 index 0000000..65db8a8 --- /dev/null +++ b/lib/adodb/adodbSQL_drivers/postgres7/postgres7_meta_module.inc @@ -0,0 +1,712 @@ + 0 AND a.atttypid = t.oid AND a.attrelid = c.oid ORDER BY a.attnum"; + + // used when schema defined + var $metaColumnsSQL1 = "SELECT a.attname, t.typname, a.attlen, a.atttypmod, a.attnotnull, a.atthasdef, a.attnum +FROM pg_class c, pg_attribute a, pg_type t, pg_namespace n +WHERE relkind in ('r','v') AND (c.relname='%s' or c.relname = lower('%s')) + and c.relnamespace=n.oid and n.nspname='%s' + and a.attname not like '....%%' AND a.attnum > 0 + AND a.atttypid = t.oid AND a.attrelid = c.oid ORDER BY a.attnum"; + + // get primary key etc -- from Freek Dijkstra + var $metaKeySQL = "SELECT ic.relname AS index_name, a.attname AS column_name,i.indisunique AS unique_key, i.indisprimary AS primary_key + FROM pg_class bc, pg_class ic, pg_index i, pg_attribute a WHERE bc.oid = i.indrelid AND ic.oid = i.indexrelid AND (i.indkey[0] = a.attnum OR i.indkey[1] = a.attnum OR i.indkey[2] = a.attnum OR i.indkey[3] = a.attnum OR i.indkey[4] = a.attnum OR i.indkey[5] = a.attnum OR i.indkey[6] = a.attnum OR i.indkey[7] = a.attnum) AND a.attrelid = bc.oid AND bc.relname = '%s'"; + var $metaDefaultsSQL = "SELECT d.adnum as num, d.adsrc as def from pg_attrdef d, pg_class c where d.adrelid=c.oid and c.relname='%s' order by d.adnum"; + + function MetaError($err=false) + { + include_once(ADODB_DIR."/adodb-error.inc.php"); + if ($err === false) + $err = $this->ErrorNo(); + + return adodb_error($this->dataProvider,$this->databaseType,$err); + } + + function MetaErrorMsg($errno) + { + include_once(ADODB_DIR."/adodb-error.inc.php"); + return adodb_errormsg($errno); + } + + /** + * @returns an array with the primary key columns in it. + */ + function MetaPrimaryKeys($table, $owner=false) + { + // owner not used in base class - see oci8 + $p = array(); + $objs =& $this->MetaColumns($table); + if ($objs) { + foreach($objs as $v) { + if (!empty($v->primary_key)) + $p[] = $v->name; + } + } + if (sizeof($p)) return $p; + if (function_exists('ADODB_VIEW_PRIMARYKEYS')) + return ADODB_VIEW_PRIMARYKEYS($this->databaseType, $this->database, $table, $owner); + return false; + } + + /** + * @returns assoc array where keys are tables, and values are foreign keys + */ + function MetaForeignKeys($table, $owner=false, $upper=false) + { + $sql = 'SELECT t.tgargs as args + FROM + pg_trigger t,pg_class c,pg_proc p + WHERE + t.tgenabled AND + t.tgrelid = c.oid AND + t.tgfoid = p.oid AND + p.proname = \'RI_FKey_check_ins\' AND + c.relname = \''.strtolower($table).'\' + ORDER BY + t.tgrelid'; + + $rs =& $this->Execute($sql); + + if (!$rs || $rs->EOF) return false; + + $arr =& $rs->GetArray(); + $a = array(); + foreach($arr as $v) { + $data = explode(chr(0), $v['args']); + $size = count($data)-1; //-1 because the last node is empty + for($i = 4; $i < $size; $i++) { + if ($upper) + $a[strtoupper($data[2])][] = strtoupper($data[$i].'='.$data[++$i]); + else + $a[$data[2]][] = $data[$i].'='.$data[++$i]; + } + } + return $a; + } + + // not the fastest implementation - quick and dirty - jlim + // for best performance, use the actual $rs->MetaType(). + function MetaType($t,$len=-1,$fieldobj=false) + { + if (empty($this->_metars)) { + $rsclass = $this->last_module_name . "_ResultSet"; + $this->_metars =& new $rsclass(false,$this->fetchMode); + } + + return $this->_metars->MetaType($t,$len,$fieldobj); + } + + /** + * return the databases that the driver can connect to. + * Some databases will return an empty array. + * + * @return an array of database names. + */ + function MetaDatabases() + { + global $ADODB_FETCH_MODE; + + if ($this->metaDatabasesSQL) { + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + + if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); + + $arr = $this->GetCol($this->metaDatabasesSQL); + if (isset($savem)) $this->SetFetchMode($savem); + $ADODB_FETCH_MODE = $save; + + return $arr; + } + + return false; + } + + /** + * @param ttype can either be 'VIEW' or 'TABLE' or false. + * If false, both views and tables are returned. + * "VIEW" returns only views + * "TABLE" returns only tables + * @param showSchema returns the schema/user with the table name, eg. USER.TABLE + * @param mask is the input mask - only supported by oci8 and postgresql + * + * @return array of tables for current database. + */ + function &MetaTables($ttype=false,$showSchema=false,$mask=false) + { + $info = $this->ServerInfo(); + if ($info['version'] >= 7.3) { + $this->metaTablesSQL = "select tablename,'T' from pg_tables where tablename not like 'pg\_%' + and schemaname not in ( 'pg_catalog','information_schema') + union + select viewname,'V' from pg_views where viewname not like 'pg\_%' and schemaname not in ( 'pg_catalog','information_schema') "; + } + if ($mask) { + $save = $this->metaTablesSQL; + $mask = $this->qstr(strtolower($mask)); + if ($info['version']>=7.3) + $this->metaTablesSQL = " +select tablename,'T' from pg_tables where tablename like $mask and schemaname not in ( 'pg_catalog','information_schema') + union +select viewname,'V' from pg_views where viewname like $mask and schemaname not in ( 'pg_catalog','information_schema') "; + else + $this->metaTablesSQL = " +select tablename,'T' from pg_tables where tablename like $mask + union +select viewname,'V' from pg_views where viewname like $mask"; + } + $ret =& $this->_MetaTables($ttype,$showSchema); + + if ($mask) { + $this->metaTablesSQL = $save; + } + return $ret; + } + + function &_MetaTables($ttype=false,$showSchema=false,$mask=false) + { + global $ADODB_FETCH_MODE; + + $false = false; + if ($mask) { + return $false; + } + if ($this->metaTablesSQL) { + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + + if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); + + $rs = $this->Execute($this->metaTablesSQL); + if (isset($savem)) $this->SetFetchMode($savem); + $ADODB_FETCH_MODE = $save; + + if ($rs === false) return $false; + $arr =& $rs->GetArray(); + $arr2 = array(); + + if ($hast = ($ttype && isset($arr[0][1]))) { + $showt = strncmp($ttype,'T',1); + } + + for ($i=0; $i < sizeof($arr); $i++) { + if ($hast) { + if ($showt == 0) { + if (strncmp($arr[$i][1],'T',1) == 0) $arr2[] = trim($arr[$i][0]); + } else { + if (strncmp($arr[$i][1],'V',1) == 0) $arr2[] = trim($arr[$i][0]); + } + } else + $arr2[] = trim($arr[$i][0]); + } + $rs->Close(); + return $arr2; + } + return $false; + } + + function _findschema(&$table,&$schema) + { + if (!$schema && ($at = strpos($table,'.')) !== false) { + $schema = substr($table,0,$at); + $table = substr($table,$at+1); + } + } + + /** + * List columns in a database as an array of ADOFieldObjects. + * See top of file for definition of object. + * + * @param $table table name to query + * @param $normalize makes table name case-insensitive (required by some databases) + * @schema is optional database schema to use - not supported by all databases. + * + * @return array of ADOFieldObjects for current table. + */ + function &MetaColumns($table,$normalize=true) + { + global $ADODB_FETCH_MODE; + + $schema = false; + $false = false; + $this->_findschema($table,$schema); + + if ($normalize) $table = strtolower($table); + + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); + + if ($schema) $rs =& $this->Execute(sprintf($this->metaColumnsSQL1,$table,$table,$schema)); + else $rs =& $this->Execute(sprintf($this->metaColumnsSQL,$table,$table)); + if (isset($savem)) $this->SetFetchMode($savem); + $ADODB_FETCH_MODE = $save; + + if ($rs === false) { + return $false; + } + if (!empty($this->metaKeySQL)) { + // If we want the primary keys, we have to issue a separate query + // Of course, a modified version of the metaColumnsSQL query using a + // LEFT JOIN would have been much more elegant, but postgres does + // not support OUTER JOINS. So here is the clumsy way. + + $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; + + $rskey = $this->Execute(sprintf($this->metaKeySQL,($table))); + // fetch all result in once for performance. + $keys =& $rskey->GetArray(); + if (isset($savem)) $this->SetFetchMode($savem); + $ADODB_FETCH_MODE = $save; + + $rskey->Close(); + unset($rskey); + } + + $rsdefa = array(); + if (!empty($this->metaDefaultsSQL)) { + $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; + $sql = sprintf($this->metaDefaultsSQL, ($table)); + $rsdef = $this->Execute($sql); + if (isset($savem)) $this->SetFetchMode($savem); + $ADODB_FETCH_MODE = $save; + + if ($rsdef) { + while (!$rsdef->EOF) { + $num = $rsdef->fields['num']; + $s = $rsdef->fields['def']; + if (strpos($s,'::')===false && substr($s, 0, 1) == "'") { /* quoted strings hack... for now... fixme */ + $s = substr($s, 1); + $s = substr($s, 0, strlen($s) - 1); + } + + $rsdefa[$num] = $s; + $rsdef->MoveNext(); + } + } else { + ADOConnection::outp( "==> SQL => " . $sql); + } + unset($rsdef); + } + + $retarr = array(); + while (!$rs->EOF) { + $fld = new ADOFieldObject(); + $fld->name = $rs->fields[0]; + $fld->type = $rs->fields[1]; + $fld->max_length = $rs->fields[2]; + $fld->attnum = $rs->fields[6]; + + if ($fld->max_length <= 0) $fld->max_length = $rs->fields[3]-4; + if ($fld->max_length <= 0) $fld->max_length = -1; + if ($fld->type == 'numeric') { + $fld->scale = $fld->max_length & 0xFFFF; + $fld->max_length >>= 16; + } + // dannym + // 5 hasdefault; 6 num-of-column + $fld->has_default = ($rs->fields[5] == 't'); + if ($fld->has_default) { + $fld->default_value = $rsdefa[$rs->fields[6]]; + } + + //Freek + $fld->not_null = $rs->fields[4] == 't'; + + + // Freek + if (is_array($keys)) { + foreach($keys as $key) { + if ($fld->name == $key['column_name'] AND $key['primary_key'] == 't') + $fld->primary_key = true; + if ($fld->name == $key['column_name'] AND $key['unique_key'] == 't') + $fld->unique = true; // What name is more compatible? + } + } + + if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld; + else $retarr[($normalize) ? strtoupper($fld->name) : $fld->name] = $fld; + + $rs->MoveNext(); + } + $rs->Close(); + if (empty($retarr)) + return $false; + else + return $retarr; + + } + + /** + * List indexes on a table as an array. + * @param table table name to query + * @param primary true to only show primary keys. Not actually used for most databases + * + * @return array of indexes on current table. Each element represents an index, and is itself an associative array. + + Array ( + [name_of_index] => Array + ( + [unique] => true or false + [columns] => Array + ( + [0] => firstname + [1] => lastname + ) + ) + */ + function &MetaIndexes ($table, $primary = FALSE) + { + global $ADODB_FETCH_MODE; + + $schema = false; + $this->_findschema($table,$schema); + + if ($schema) { // requires pgsql 7.3+ - pg_namespace used. + $sql = ' +SELECT c.relname as "Name", i.indisunique as "Unique", i.indkey as "Columns" +FROM pg_catalog.pg_class c +JOIN pg_catalog.pg_index i ON i.indexrelid=c.oid +JOIN pg_catalog.pg_class c2 ON c2.oid=i.indrelid + ,pg_namespace n +WHERE (c2.relname=\'%s\' or c2.relname=lower(\'%s\')) and c.relnamespace=c2.relnamespace and c.relnamespace=n.oid and n.nspname=\'%s\''; + } else { + $sql = ' +SELECT c.relname as "Name", i.indisunique as "Unique", i.indkey as "Columns" +FROM pg_catalog.pg_class c +JOIN pg_catalog.pg_index i ON i.indexrelid=c.oid +JOIN pg_catalog.pg_class c2 ON c2.oid=i.indrelid +WHERE (c2.relname=\'%s\' or c2.relname=lower(\'%s\'))'; + } + + if ($primary == FALSE) { + $sql .= ' AND i.indisprimary=false;'; + } + + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + if ($this->fetchMode !== FALSE) { + $savem = $this->SetFetchMode(FALSE); + } + + $rs = $this->Execute(sprintf($sql,$table,$table,$schema)); + if (isset($savem)) { + $this->SetFetchMode($savem); + } + $ADODB_FETCH_MODE = $save; + + if (!is_object($rs)) { + $false = false; + return $false; + } + + $col_names = $this->MetaColumnNames($table,true,true); + //3rd param is use attnum, + // see http://sourceforge.net/tracker/index.php?func=detail&aid=1451245&group_id=42718&atid=433976 + $indexes = array(); + while ($row = $rs->FetchRow()) { + $columns = array(); + foreach (explode(' ', $row[2]) as $col) { + $columns[] = $col_names[$col]; + } + + $indexes[$row[0]] = array( + 'unique' => ($row[1] == 't'), + 'columns' => $columns + ); + } + return $indexes; + } + + /** + * List columns names in a table as an array. + * @param table table name to query + * + * @return array of column names for current table. + */ + function &MetaColumnNames($table, $numIndexes=false,$useattnum=false /* only for postgres */) + { + $objarr =& $this->MetaColumns($table); + if (!is_array($objarr)) { + $false = false; + return $false; + } + $arr = array(); + if ($numIndexes) { + $i = 0; + if ($useattnum) { + foreach($objarr as $v) + $arr[$v->attnum] = $v->name; + + } else + foreach($objarr as $v) $arr[$i++] = $v->name; + } else + foreach($objarr as $v) $arr[strtoupper($v->name)] = $v->name; + + return $arr; + } + + function MetaTransaction($mode,$db) + { + $mode = strtoupper($mode); + $mode = str_replace('ISOLATION LEVEL ','',$mode); + + switch($mode) { + + case 'READ UNCOMMITTED': + switch($db) { + case 'oci8': + case 'oracle': + return 'ISOLATION LEVEL READ COMMITTED'; + default: + return 'ISOLATION LEVEL READ UNCOMMITTED'; + } + break; + + case 'READ COMMITTED': + return 'ISOLATION LEVEL READ COMMITTED'; + break; + + case 'REPEATABLE READ': + switch($db) { + case 'oci8': + case 'oracle': + return 'ISOLATION LEVEL SERIALIZABLE'; + default: + return 'ISOLATION LEVEL REPEATABLE READ'; + } + break; + + case 'SERIALIZABLE': + return 'ISOLATION LEVEL SERIALIZABLE'; + break; + + default: + return $mode; + } + } + +} + +eval('class postgres7_meta_resultset_EXTENDER extends '. $last_module . '_ResultSet { }'); + +class postgres7_meta_ResultSet extends postgres7_meta_resultset_EXTENDER +{ + /** + * Get the metatype of the column. This is used for formatting. This is because + * many databases use different names for the same type, so we transform the original + * type to our standardised version which uses 1 character codes: + * + * @param t is the type passed in. Normally is ADOFieldObject->type. + * @param len is the maximum length of that field. This is because we treat character + * fields bigger than a certain size as a 'B' (blob). + * @param fieldobj is the field object returned by the database driver. Can hold + * additional info (eg. primary_key for mysql). + * + * @return the general type of the data: + * C for character < 250 chars + * X for teXt (>= 250 chars) + * B for Binary + * N for numeric or floating point + * D for date + * T for timestamp + * L for logical/Boolean + * I for integer + * R for autoincrement counter/integer + * + * + */ + function MetaType($t,$len=-1,$fieldobj=false) + { + if (is_object($t)) { + $fieldobj = $t; + $t = $fieldobj->type; + $len = $fieldobj->max_length; + } + $is_serial = is_object($fieldobj) && $fieldobj->primary_key && $fieldobj->unique && $fieldobj->has_default && substr($fieldobj->default_value,0,8) == 'nextval('; + switch (strtoupper($t)) { + case 'MONEY': // stupid, postgres expects money to be a string + case 'INTERVAL': + case 'CHAR': + case 'CHARACTER': + case 'VARCHAR': + case 'NAME': + case 'BPCHAR': + case '_VARCHAR': + case 'INET': + case 'MACADDR': + if ($len <= $this->blobSize) return 'C'; + case 'TEXT': + return 'X'; + case 'IMAGE': // user defined type + case 'BLOB': // user defined type + case 'BIT': // This is a bit string, not a single bit, so don't return 'L' + case 'VARBIT': + case 'BYTEA': + return 'B'; + case 'BOOL': + case 'BOOLEAN': + return 'L'; + case 'DATE': + return 'D'; + case 'TIMESTAMP WITHOUT TIME ZONE': + case 'TIME': + case 'DATETIME': + case 'TIMESTAMP': + case 'TIMESTAMPTZ': + return 'T'; + case 'INTEGER': return !$is_serial ? 'I' : 'R'; + case 'SMALLINT': + case 'INT2': return !$is_serial ? 'I2' : 'R'; + case 'INT4': return !$is_serial ? 'I4' : 'R'; + case 'BIGINT': + case 'INT8': return !$is_serial ? 'I8' : 'R'; + case 'OID': + case 'SERIAL': + return 'R'; + case 'FLOAT4': + case 'FLOAT8': + case 'DOUBLE PRECISION': + case 'REAL': + return 'F'; + default: + static $typeMap = array( + 'VARCHAR' => 'C', + 'VARCHAR2' => 'C', + 'CHAR' => 'C', + 'C' => 'C', + 'STRING' => 'C', + 'NCHAR' => 'C', + 'NVARCHAR' => 'C', + 'VARYING' => 'C', + 'BPCHAR' => 'C', + 'CHARACTER' => 'C', + 'INTERVAL' => 'C', # Postgres + 'MACADDR' => 'C', # postgres + ## + 'LONGCHAR' => 'X', + 'TEXT' => 'X', + 'NTEXT' => 'X', + 'M' => 'X', + 'X' => 'X', + 'CLOB' => 'X', + 'NCLOB' => 'X', + 'LVARCHAR' => 'X', + ## + 'BLOB' => 'B', + 'IMAGE' => 'B', + 'BINARY' => 'B', + 'VARBINARY' => 'B', + 'LONGBINARY' => 'B', + 'B' => 'B', + ## + 'YEAR' => 'D', // mysql + 'DATE' => 'D', + 'D' => 'D', + ## + 'UNIQUEIDENTIFIER' => 'C', # MS SQL Server + ## + 'TIME' => 'T', + 'TIMESTAMP' => 'T', + 'DATETIME' => 'T', + 'TIMESTAMPTZ' => 'T', + 'T' => 'T', + 'TIMESTAMP WITHOUT TIME ZONE' => 'T', // postgresql + ## + 'BOOL' => 'L', + 'BOOLEAN' => 'L', + 'BIT' => 'L', + 'L' => 'L', + ## + 'COUNTER' => 'R', + 'R' => 'R', + 'SERIAL' => 'R', // ifx + 'INT IDENTITY' => 'R', + ## + 'INT' => 'I', + 'INT2' => 'I', + 'INT4' => 'I', + 'INT8' => 'I', + 'INTEGER' => 'I', + 'INTEGER UNSIGNED' => 'I', + 'SHORT' => 'I', + 'TINYINT' => 'I', + 'SMALLINT' => 'I', + 'I' => 'I', + ## + 'LONG' => 'N', // interbase is numeric, oci8 is blob + 'BIGINT' => 'N', // this is bigger than PHP 32-bit integers + 'DECIMAL' => 'N', + 'DEC' => 'N', + 'REAL' => 'N', + 'DOUBLE' => 'N', + 'DOUBLE PRECISION' => 'N', + 'SMALLFLOAT' => 'N', + 'FLOAT' => 'N', + 'NUMBER' => 'N', + 'NUM' => 'N', + 'NUMERIC' => 'N', + 'MONEY' => 'N', + + ## informix 9.2 + 'SQLINT' => 'I', + 'SQLSERIAL' => 'I', + 'SQLSMINT' => 'I', + 'SQLSMFLOAT' => 'N', + 'SQLFLOAT' => 'N', + 'SQLMONEY' => 'N', + 'SQLDECIMAL' => 'N', + 'SQLDATE' => 'D', + 'SQLVCHAR' => 'C', + 'SQLCHAR' => 'C', + 'SQLDTIME' => 'T', + 'SQLINTERVAL' => 'N', + 'SQLBYTES' => 'B', + 'SQLTEXT' => 'X', + ## informix 10 + "SQLINT8" => 'I8', + "SQLSERIAL8" => 'I8', + "SQLNCHAR" => 'C', + "SQLNVCHAR" => 'C', + "SQLLVARCHAR" => 'X', + "SQLBOOL" => 'L' + ); + + $tmap = false; + $t = strtoupper($t); + $tmap = (isset($typeMap[$t])) ? $typeMap[$t] : 'N'; + if ($t == 'LONG' && $this->dataProvider == 'oci8') return 'B'; + return $tmap; + } + } + +} + +?> \ No newline at end of file diff --git a/lib/adodb/adodbSQL_drivers/postgres7/postgres7_transaction_module.inc b/lib/adodb/adodbSQL_drivers/postgres7/postgres7_transaction_module.inc new file mode 100644 index 0000000..6cdc61e --- /dev/null +++ b/lib/adodb/adodbSQL_drivers/postgres7/postgres7_transaction_module.inc @@ -0,0 +1,128 @@ +transOff > 0) { + $this->transOff += 1; + return; + } + $this->transaction_status = true; + + if ($this->debug && $this->transCnt > 0) + ADOConnection::outp("Bad Transaction: StartTrans called within BeginTrans"); + + $this->BeginTrans(); + $this->transOff = 1; + } + + function BeginTrans() + { + if ($this->transOff) + return true; + + $this->transCnt += 1; + return @pg_Exec($this->connectionId, "begin"); + } + + function CompleteTrans($autoComplete = true) + { + if ($this->transOff > 1) { + $this->transOff -= 1; + return true; + } + $this->transOff = 0; + if ($this->transaction_status && $autoComplete) { + if (!$this->CommitTrans()) { + $this->transaction_status = false; + if ($this->debug) + ADOConnection::outp("Smart Commit failed"); + } else + if ($this->debug) + ADOConnection::outp("Smart Commit occurred"); + } else { + $this->RollbackTrans(); + if ($this->debug) + ADOCOnnection::outp("Smart Rollback occurred"); + } + return $this->transaction_status; + } + + function CommitTrans($ok=true) + { + if ($this->transOff) + return true; + + if (!$ok) + return $this->RollbackTrans(); + + $this->transCnt -= 1; + return @pg_Exec($this->connectionId, "commit"); + } + + function RollbackTrans() + { + if ($this->transOff) + return true; + + $this->transCnt -= 1; + return @pg_Exec($this->connectionId, "rollback"); + } + + function FailTrans() + { + if ($this->debug) + if ($this->transOff == 0) { + ADOConnection::outp("FailTrans outside StartTrans/CompleteTrans"); + } else { + ADOConnection::outp("FailTrans was called"); + } + $this->transaction_status = false; + } + + function HasFailedTrans() + { + if ($this->transOff > 0) + return $this->transaction_status == false; + + return false; + } + + function RowLock($tables,$where,$flds='1 as ignore') + { + if (!$this->transCnt) + $this->BeginTrans(); + + return $this->GetOne("select $flds from $tables where $where for update"); + } + + function CommitLock($table) + { + return $this->CommitTrans(); + } + + function RollbackLock($table) + { + return $this->RollbackTrans(); + } + +} + +eval('class postgres7_transaction_resultset_EXTENDER extends '. $last_module . '_ResultSet { }'); + +class postgres7_transaction_ResultSet extends postgres7_transaction_resultset_EXTENDER +{ +} +?> \ No newline at end of file diff --git a/lib/adodb/adodbSQL_drivers/postgres8/postgres8_datadict.inc b/lib/adodb/adodbSQL_drivers/postgres8/postgres8_datadict.inc new file mode 100644 index 0000000..c8c7f9f --- /dev/null +++ b/lib/adodb/adodbSQL_drivers/postgres8/postgres8_datadict.inc @@ -0,0 +1,211 @@ +TableName ($tabname); + $sql = array(); + list($lines,$pkey) = $this->_GenFields($flds); + $alter = 'ALTER TABLE ' . $tabname . $this->addCol . ' '; + foreach($lines as $v) { + if (($not_null = preg_match('/NOT NULL/i',$v))) { + $v = preg_replace('/NOT NULL/i','',$v); + } + if (preg_match('/^([^ ]+) .*(DEFAULT [^ ]+)/',$v,$matches)) { + list(,$colname,$default) = $matches; + $sql[] = $alter . str_replace($default,'',$v); + $sql[] = 'ALTER TABLE '.$tabname.' ALTER COLUMN '.$colname.' SET ' . $default; + } else { + $sql[] = $alter . $v; + } + if ($not_null) { + list($colname) = explode(' ',$v); + $sql[] = 'ALTER TABLE '.$tabname.' ALTER COLUMN '.$colname.' SET NOT NULL'; + } + } + return $sql; + } + + function AlterColumnSQL($tabname, $flds, $tableflds='',$tableoptions='') + { + if (!$tableflds) { + if ($this->debug) $this->outp("AlterColumnSQL needs a complete table-definiton for PostgreSQL"); + return array(); + } + return $this->_recreate_copy_table($tabname,False,$tableflds,$tableoptions); + } + + function DropColumnSQL($tabname, $flds, $tableflds='',$tableoptions='') + { + $has_drop_column = 7.3 <= (float) @$this->serverInfo['version']; + if (!$has_drop_column && !$tableflds) { + if ($this->debug) $this->outp("DropColumnSQL needs complete table-definiton for PostgreSQL < 7.3"); + return array(); + } + if ($has_drop_column) { + return ADODB_DataDict::DropColumnSQL($tabname, $flds); + } + return $this->_recreate_copy_table($tabname,$flds,$tableflds,$tableoptions); + } + + function DropTableSQL($tabname) + { + $sql = ADODB_DataDict::DropTableSQL($tabname); + $drop_seq = $this->_DropAutoIncrement($tabname); + if ($drop_seq) $sql[] = $drop_seq; + return $sql; + } + + function _CreateSuffix($fname, &$ftype, $fnotnull,$fdefault,$fautoinc,$fconstraint) + { + if ($fautoinc) { + $ftype = 'SERIAL'; + return ''; + } + $suffix = ''; + if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault"; + if ($fnotnull) $suffix .= ' NOT NULL'; + if ($fconstraint) $suffix .= ' '.$fconstraint; + return $suffix; + } + + function _DropAutoIncrement($tabname) + { + $tabname = $this->connection->qstr('%'.$tabname.'%'); + + $seq = $this->connection->GetOne("SELECT relname FROM pg_class WHERE NOT relname ~ 'pg_.*' AND relname LIKE $tabname AND relkind='S'"); + + // check if a tables depends on the sequenz and it therefor cant and dont need to be droped separatly + if (!$seq || $this->connection->GetOne("SELECT relname FROM pg_class JOIN pg_depend ON pg_class.relfilenode=pg_depend.objid WHERE relname='$seq' AND relkind='S' AND deptype='i'")) { + return False; + } + return "DROP SEQUENCE ".$seq; + } + + function _IndexSQL($idxname, $tabname, $flds, $idxoptions) + { + $sql = array(); + if ( isset($idxoptions['REPLACE']) || isset($idxoptions['DROP']) ) { + $sql[] = sprintf ($this->dropIndex, $idxname, $tabname); + if ( isset($idxoptions['DROP']) ) + return $sql; + } + if ( empty ($flds) ) { + return $sql; + } + + $unique = isset($idxoptions['UNIQUE']) ? ' UNIQUE' : ''; + $s = 'CREATE' . $unique . ' INDEX ' . $idxname . ' ON ' . $tabname . ' '; + if (isset($idxoptions['HASH'])) + $s .= 'USING HASH '; + + if ( isset($idxoptions[$this->upperName]) ) + $s .= $idxoptions[$this->upperName]; + + if ( is_array($flds) ) + $flds = implode(', ',$flds); + $s .= '(' . $flds . ')'; + $sql[] = $s; + return $sql; + } + + + function _recreate_copy_table($tabname,$dropflds,$tableflds,$tableoptions='') + { + if ($dropflds && !is_array($dropflds)) $dropflds = explode(',',$dropflds); + $copyflds = array(); + foreach($this->MetaColumns($tabname) as $fld) { + if (!$dropflds || !in_array($fld->name,$dropflds)) { + // we need to explicit convert varchar to a number to be able to do an AlterColumn of a char column to a nummeric one + if (preg_match('/'.$fld->name.' (I|I2|I4|I8|N|F)/i',$tableflds,$matches) && + in_array($fld->type,array('varchar','char','text','bytea'))) { + $copyflds[] = "to_number($fld->name,'S99D99')"; + } else { + $copyflds[] = $fld->name; + } + // identify the sequence name and the fld its on + if ($fld->primary_key && $fld->has_default && + preg_match("/nextval\('([^']+)'::text\)/",$fld->default_value,$matches)) { + $seq_name = $matches[1]; + $seq_fld = $fld->name; + } + } + } + $copyflds = implode(', ',$copyflds); + + $tempname = $tabname.'_tmp'; + $aSql[] = 'BEGIN'; // we use a transaction, to make sure not to loose the content of the table + $aSql[] = "SELECT * INTO TEMPORARY TABLE $tempname FROM $tabname"; + $aSql = array_merge($aSql,$this->DropTableSQL($tabname)); + $aSql = array_merge($aSql,$this->CreateTableSQL($tabname,$tableflds,$tableoptions)); + $aSql[] = "INSERT INTO $tabname SELECT $copyflds FROM $tempname"; + if ($seq_name && $seq_fld) { // if we have a sequence we need to set it again + $seq_name = $tabname.'_'.$seq_fld.'_seq'; // has to be the name of the new implicit sequence + $aSql[] = "SELECT setval('$seq_name',MAX($seq_fld)) FROM $tabname"; + } + $aSql[] = "DROP TABLE $tempname"; + // recreate the indexes, if they not contain one of the droped columns + foreach($this->MetaIndexes($tabname) as $idx_name => $idx_data) + { + if (substr($idx_name,-5) != '_pkey' && (!$dropflds || !count(array_intersect($dropflds,$idx_data['columns'])))) { + $aSql = array_merge($aSql,$this->CreateIndexSQL($idx_name,$tabname,$idx_data['columns'], + $idx_data['unique'] ? array('UNIQUE') : False)); + } + } + $aSql[] = 'COMMIT'; + return $aSql; + } +} + +?> \ No newline at end of file diff --git a/lib/adodb/adodbSQL_drivers/postgres8/postgres8_date_module.inc b/lib/adodb/adodbSQL_drivers/postgres8/postgres8_date_module.inc new file mode 100644 index 0000000..99a4ad7 --- /dev/null +++ b/lib/adodb/adodbSQL_drivers/postgres8/postgres8_date_module.inc @@ -0,0 +1,316 @@ +_Execute("select $this->sysTimeStamp"); + if ($rs && !$rs->EOF) + return $this->UnixTimeStamp(reset($rs->fields)); + + return false; + } + + function OffsetDate($dayFraction, $date=false) + { + if (!$date) + $date = $this->sysDate; + else if (strncmp($date,"'",1) == 0) { + $len = strlen($date); + if (10 <= $len && $len <= 12) + $date = 'date ' . $date; + else $date = 'timestamp ' . $date; + } + return "($date+interval'$dayFraction days')"; + } + + function SetDateLocale($locale = 'En') + { + $this->locale = $locale; + switch (strtoupper($locale)) + { + case 'EN': + $this->fmtDate="'Y-m-d'"; + $this->fmtTimeStamp = "'Y-m-d H:i:s'"; + break; + + case 'US': + $this->fmtDate = "'m-d-Y'"; + $this->fmtTimeStamp = "'m-d-Y H:i:s'"; + break; + + case 'NL': + case 'FR': + case 'RO': + case 'IT': + $this->fmtDate="'d-m-Y'"; + $this->fmtTimeStamp = "'d-m-Y H:i:s'"; + break; + + case 'GE': + $this->fmtDate="'d.m.Y'"; + $this->fmtTimeStamp = "'d.m.Y H:i:s'"; + break; + + default: + $this->fmtDate="'Y-m-d'"; + $this->fmtTimeStamp = "'Y-m-d H:i:s'"; + break; + } + } + + function DBDate($date) + { + if (empty($date) && $date !== 0) + return 'null'; + + if (is_string($date) && !is_numeric($date)) { + if ($date === 'null' || strncmp($date, "'", 1) === 0) + return $date; + + if ($this->isoDates) + return "'$date'"; + + $date = $this->UnixDate($date); + } + + return adodb_date($this->fmtDate,$date); + } + + function DBTimeStamp($timestamp) + { + if (empty($timestamp) && $timestamp !== 0) + return 'null'; + + # strlen(14) allows YYYYMMDDHHMMSS format + if (!is_string($timestamp) || (is_numeric($timestamp) && strlen($timestamp)<14)) + return adodb_date($this->fmtTimeStamp, $timestamp); + + if ($timestamp === 'null') + return $timestamp; + + if ($this->isoDates && strlen($timestamp) !== 14) + return "'$timestamp'"; + + $timestamp = $this->UnixTimeStamp($timestamp); + return adodb_date($this->fmtTimeStamp, $timestamp); + } + + function UnixDate($v) + { + if (is_object($v)) { + // odbtp support + //( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 ) + return adodb_mktime($v->hour, $v->minute, $v->second, $v->month, $v->day, $v->year); + } + + if (is_numeric($v) && strlen($v) !== 8) + return $v; + + if (!preg_match( "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})|", ($v), $rr)) + return false; + + if ($rr[1] <= TIMESTAMP_FIRST_YEAR) + return 0; + + // h-m-s-MM-DD-YY + return adodb_mktime(0, 0, 0, $rr[2], $rr[3], $rr[1]); + } + + function UnixTimeStamp($v) + { + if (is_object($v)) { + // odbtp support + //( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 ) + return adodb_mktime($v->hour, $v->minute, $v->second, $v->month, $v->day, $v->year); + } + + if (!preg_match("|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ ,-]*(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,4}))?|", ($v), $rr)) + return false; + + if ($rr[1] <= TIMESTAMP_FIRST_YEAR && $rr[2]<= 1) + return 0; + + // h-m-s-MM-DD-YY + if (!isset($rr[5])) + return adodb_mktime(0, 0, 0, $rr[2], $rr[3], $rr[1]); + else return adodb_mktime($rr[5], $rr[6], $rr[7], $rr[2], $rr[3], $rr[1]); + } + + function UserDate($v, $fmt='Y-m-d', $gmt=false) + { + $tt = $this->UnixDate($v); + + // $tt == -1 if pre TIMESTAMP_FIRST_YEAR + if (($tt === false || $tt == -1) && $v != false) + return $v; + else if ($tt == 0) + return $this->emptyDate; + else if ($tt == -1) { // pre-TIMESTAMP_FIRST_YEAR + } + + return ($gmt) ? adodb_gmdate($fmt, $tt) : adodb_date($fmt, $tt); + } + + function UserTimeStamp($v, $fmt='Y-m-d H:i:s', $gmt=false) + { + if (!isset($v)) + return $this->emptyTimeStamp; + + # strlen(14) allows YYYYMMDDHHMMSS format + if (is_numeric($v) && strlen($v)<14) + return ($gmt) ? adodb_gmdate($fmt,$v) : adodb_date($fmt,$v); + + $tt = $this->UnixTimeStamp($v); + // $tt == -1 if pre TIMESTAMP_FIRST_YEAR + if (($tt === false || $tt == -1) && $v != false) + return $v; + + if ($tt == 0) + return $this->emptyTimeStamp; + else return ($gmt) ? adodb_gmdate($fmt,$tt) : adodb_date($fmt,$tt); + } + + function SQLDate($fmt, $col=false) + { + if (!$col) + $col = $this->sysTimeStamp; + + $s = 'TO_CHAR('.$col.",'"; + $len = strlen($fmt); + for ($i=0; $i < $len; $i++) { + $ch = $fmt[$i]; + switch($ch) { + case 'Y': + case 'y': + $s .= 'YYYY'; + break; + + case 'Q': + case 'q': + $s .= 'Q'; + break; + + case 'M': + $s .= 'Mon'; + break; + + case 'm': + $s .= 'MM'; + break; + + case 'D': + case 'd': + $s .= 'DD'; + break; + + case 'H': + $s.= 'HH24'; + break; + + case 'h': + $s .= 'HH'; + break; + + case 'i': + $s .= 'MI'; + break; + + case 's': + $s .= 'SS'; + break; + + case 'a': + case 'A': + $s .= 'AM'; + break; + + case 'w': + $s .= 'D'; + break; + + case 'l': + $s .= 'DAY'; + break; + + default: + // handle escape characters... + if ($ch == '\\') { + $i++; + $ch = substr($fmt,$i,1); + } + if (strpos('-/.:;, ',$ch) !== false) + $s .= $ch; + else $s .= '"'.$ch.'"'; + } + } + return $s. "')"; + } +} + +eval('class postgres8_date_resultset_EXTENDER extends '. $last_module . '_ResultSet { }'); + +class postgres8_date_ResultSet extends postgres8_date_resultset_EXTENDER +{ + var $emptyTimeStamp = ' '; /// what to display when $time==0 + var $emptyDate = ' '; /// what to display when $time==0 + var $datetime = false; + + function UserTimeStamp($v, $fmt='Y-m-d H:i:s') + { + if (is_numeric($v) && strlen($v)<14) + return adodb_date($fmt,$v); + + $tt = $this->UnixTimeStamp($v); + // $tt == -1 if pre TIMESTAMP_FIRST_YEAR + if (($tt === false || $tt == -1) && $v != false) + return $v; + + if ($tt === 0) + return $this->emptyTimeStamp; + else return adodb_date($fmt,$tt); + } + + function UserDate($v,$fmt='Y-m-d') + { + $tt = $this->UnixDate($v); + // $tt == -1 if pre TIMESTAMP_FIRST_YEAR + if (($tt === false || $tt == -1) && $v != false) + return $v; + else if ($tt == 0) + return $this->emptyDate; + else if ($tt == -1) { // pre-TIMESTAMP_FIRST_YEAR + } + return adodb_date($fmt,$tt); + } + + function UnixDate($v) + { + return postgres8_date_ADOConnection::UnixDate($v); + } + + function UnixTimeStamp($v) + { + return postgres8_date_ADOConnection::UnixTimeStamp($v); + } + +} +?> \ No newline at end of file diff --git a/lib/adodb/adodbSQL_drivers/postgres8/postgres8_driver.inc b/lib/adodb/adodbSQL_drivers/postgres8/postgres8_driver.inc new file mode 100644 index 0000000..ea60d32 --- /dev/null +++ b/lib/adodb/adodbSQL_drivers/postgres8/postgres8_driver.inc @@ -0,0 +1,663 @@ +dbtype = 'postgres8'; + $this->dataProvider = 'postgres'; + } + + /** + * Connection to database server and selected database + * + * @access private + */ + + function _connect($host = "", $username = "", $password = "", $database = "", $persistent, $forcenew) + { + if (!function_exists('pg_connect')) return false; + + $this->host = $host; + $this->username = $this->query_addslashes($username); + $this->password = $this->query_addslashes($password); + if (strlen($database) == 0) $database = 'template1'; + $this->database = $this->query_addslashes($database); + + if ($this->username || $this->password || $this->database) { + $this->connect_string = $this->host; + if ($this->connect_string) { + $host = split(":", $this->connect_string); + if ($host[0]) $this->connect_string = "host=" . $this->query_addslashes($host[0]); + else $this->connect_string = 'host=localhost'; + if (isset($host[1])) $this->connect_string .= " port=$host[1]"; + else if (!empty($this->port)) $this->connect_string .= " port=" . $this->port; + } + if ($this->username) $this->connect_string .= " user=" . $this->username; + if ($this->password) $this->connect_string .= " password=" . $this->password; + if ($this->database) $this->connect_string .= " dbname=" . $this->database; + } + else + { + $this->connect_string = $this->host; + } + + $this->persistent = $persistent; + $this->forcenewconnection = $forcenew; + + $this->_makeconnection(); + + if ($this->connectionId === false) + { + if ($fn = $this->raiseErrorFn) + $fn($this->dbtype, 'CONNECT', $this->ErrorNo(), $this->ErrorMsg(), $this->host, $this->database, $this); + return $this->SelectDB( $this->database ); + } + + return true; + } + + function _makeconnection() + { + if($this->persistent == 1) + { + $this->connectionId = @pg_pconnect( $this->connect_string ); + } + else + { + if(!$this->forcenewconnection) + { + $this->connectionId = @pg_connect( $this->connect_string ); + } + else + { + $this->connectionId = @pg_connect( $this->connect_string, PGSQL_CONNECT_FORCE_NEW ); + } + } + } + + function query_addslashes($query) + { + $len = strlen($query); + if ($len == 0) + return "''"; + if (strncmp($query,"'",1) === 0 && substr($query,$len-1) == "'") + return $s; + return "'".addslashes($query)."'"; + } + + /** + * Choose a database to connect. + * + * @param dbname is the name of the database to select + * @return true or false + * @access public + */ + + function SelectDB($dbname) + { + $this->database = $dbname; + + if ($this->connectionId === false) + { + if($this->createdatabase == true) + { + $this->connectionId = @pg_pconnect( "host=$this->host user=$this->username password=$this->password" ); + $result = @pg_query($this->connectionId, "CREATE DATABASE " . $this->database ); + if ($result === false) { // error handling if query fails + $this->connectionId = false; + return false; + } + + $this->_makeconnection(); + if($this->connectionId === false) + { + $this->connectionId = false; + return false; + } + else + { + return true; + } + } + $this->connectionId = false; + return false; + } + else + { + return true; + } + } + + /** + * Return database error message + * Usage: $errormessage =& $db->ErrorMsg(); + * + * @access public + */ + + function ErrorMsg() + { + return @pg_last_error($this->connectionId); + } + + /** + * Return database error number + * Usage: $errorbo =& $db->ErrorNo(); + * + * @access public + */ + + function ErrorNo() + { + $error = @pg_last_error( $this->connectionId ); + return strlen($error) ? $error : 0; + } + + /** + * Returns # of affected rows from insert/delete/update query + * + * @access public + * @return integer Affected rows + */ + + function Affected_Rows() + { + return @pg_affected_rows( $this->record_set ); + } + + /** + * Returns the last record id of an inserted item + * Usage: $db->Insert_ID(); + * + * @access public + */ + + function Insert_ID() + { + return @pg_getlastoid($this->record_set); + } + + /** + * Correctly quotes a string so that all strings are escape coded. + * An example is $db->qstr("Haven't a clue."); + * + * @param string the string to quote + * @param [magic_quotes] if $s is GET/POST var, set to get_magic_quotes_gpc(). + * + * @return single-quoted string IE: 'Haven\'t a clue.' + */ + + function qstr($string, $magic_quotes=false) + { + // Ugly hack alert! Repalce backticks with some arbitrary character + $string = str_replace('`', "\x1C", $string); + if (!$magic_quotes) { + if (strnatcmp(PHP_VERSION, '4.2.0') >= 0) { + return "'" . pg_escape_string($string) . "'"; + } + $string = str_replace("'", "\\'", str_replace('\\', '\\\\', str_replace("\0", "\\\0", $string))); + return "'" . $string . "'"; + } + return "'" . str_replace('\\"', '"', $string) . "'"; + } + + function QMagic($string) + { + return $this->qstr($string, get_magic_quotes_gpc()); + } + + /** + * Returns concatenated string + * Usage: $db->Concat($str1,$str2); + * + * @return concatenated string + */ + function Concat() + { + $arr = func_get_args(); + return implode("||", $arr); + } + + function IfNull( $field, $ifNull ) + { + return " coalesce($field, $ifNull) "; + } + + /** + * Closes database connection + * Usage: $db->close(); + * + * @access public + */ + + function Close() + { + @pg_close( $this->connectionId ); + $this->connectionId = false; + } + + /** + * Returns All Records in an array + * + * Usage: $db->GetAll($sql); + * @access public + */ + + function &GetAll($sql, $inputarr = false) + { + $data =& $this->GetArray($sql, $inputarr); + return $data; + } + + /** + * Returns All Records in an array + * + * Usage: $db->GetArray($sql); + * @access public + */ + + function &GetArray($sql, $inputarr = false) + { + $data = false; + $result =& $this->Execute($sql, $inputarr); + if ($result) + { + $data =& $result->GetArray(); + $result->Close(); + } + return $data; + } + + /** + * Executes SQL query and instantiates resultset methods + * + * @access private + * @return mixed Resultset methods + */ + + function &do_query( $sql, $offset, $nrows, $inputarr=false ) + { + global $ADODB_FETCH_MODE; + $sql = str_replace('`', '"', $sql); + $sql = str_replace("\x1C", "`", $sql); + $sql = str_replace('IS_DELETED', 'is_deleted', $sql); + $sql = str_replace("HIGH_PRIORITY", "", $sql); + $false = false; + + $offsetStr = ''; + $limitStr = ''; + if ($offset != -1 || $nrows != -1) + { + $offsetStr = ($offset >= 0) ? " OFFSET " . ((integer)$offset) : ''; + $limitStr = ($nrows >= 0) ? " LIMIT " . ((integer)$nrows) : ''; + } + + if ($inputarr && is_array($inputarr)) { + $sqlarr = explode('?', $sql); + if (!is_array(reset($inputarr))) $inputarr = array($inputarr); + foreach($inputarr as $arr) { + $sql = ''; $i = 0; + foreach($arr as $v) { + $sql .= $sqlarr[$i]; + switch(gettype($v)){ + case 'string': + $sql .= $this->qstr($v); + break; + case 'double': + $sql .= str_replace(',', '.', $v); + break; + case 'boolean': + $sql .= $v ? "'t'" : "'f'"; + break; + default: + if ($v === null) + $sql .= 'NULL'; + else $sql .= $v; + } + $i += 1; + } + $sql .= $sqlarr[$i]; + if ($i+1 != sizeof($sqlarr)) + return $false; + $this->sql = $sql . "$limitStr$offsetStr"; + $time_start = array_sum(explode(' ', microtime())); + $this->query_count++; + $resultId = @pg_query( $this->sql); + $time_total = (array_sum(explode(' ', microtime())) - $time_start); + $this->query_time_total += $time_total; + if($this->debug_console) + { + $this->query_list[] = $this->sql; + $this->query_list_time[] = $time_total; + $this->query_list_errors[] = $this->ErrorMsg(); + } + if($this->debug) + { + $this->outp($sql . "$limitStr$offsetStr"); + } + if ($resultId === false) { // error handling if query fails + if ($fn = $this->raiseErrorFn) + $fn($this->dbtype, 'EXECUTE', $this->ErrorNo(), $this->ErrorMsg(), $this->sql, $inputarr, $this); + return $false; + } + } + } + else + { + $this->sql = $sql . "$limitStr$offsetStr"; + $time_start = array_sum(explode(' ', microtime())); + $this->query_count++; + $resultId = @pg_query( $this->sql); + $time_total = (array_sum(explode(' ', microtime())) - $time_start); + $this->query_time_total += $time_total; + if($this->debug_console) + { + $this->query_list[] = $this->sql; + $this->query_list_time[] = $time_total; + $this->query_list_errors[] = $this->ErrorMsg(); + } + if($this->debug) + { + $this->outp($sql . "$limitStr$offsetStr"); + } + } + + if ($resultId === false) { // error handling if query fails + if ($fn = $this->raiseErrorFn) + $fn($this->dbtype, 'EXECUTE', $this->ErrorNo(), $this->ErrorMsg(), $this->sql, $inputarr, $this); + return $false; + } + + if (@pg_numfields( $resultId ) <= 0) { // return simplified recordset for inserts/updates/deletes with lower overhead + $recordset = new ADORecordSet_empty(); + $this->record_set = $recordset; + return $recordset; + } + + $resultset_name = $this->last_module_name . "_ResultSet"; + $recordset = new $resultset_name( $resultId, $this->connectionId ); + $this->record_set = $recordset; + + $recordset->_currentRow = 0; + + switch ($ADODB_FETCH_MODE) + { + case ADODB_FETCH_NUM: $recordset->fetchMode = PGSQL_NUM; break; + case ADODB_FETCH_ASSOC:$recordset->fetchMode = PGSQL_ASSOC; break; + default: + case ADODB_FETCH_DEFAULT: + case ADODB_FETCH_BOTH:$recordset->fetchMode = PGSQL_BOTH; break; + } + + $recordset->_numOfRows = @pg_numrows( $resultId ); + if( $recordset->_numOfRows == 0) + { + $recordset->EOF = true; + } + $recordset->_numOfFields = @pg_numfields( $resultId ); + $recordset->_fetch(); + + return $recordset; + } +} + +class postgres8_driver_ResultSet +{ + var $connectionId; + var $fields; + var $resultId; + var $_currentRow = 0; + var $_numOfRows = -1; + var $_numOfFields = -1; + var $fetchMode; + var $EOF; + + /** + * pgsqlResultSet Constructor + * + * @access private + * @param string $record + * @param string $resultId + */ + + function postgres8_driver_ResultSet( $resultId, $connectionId ) + { + $this->fields = array(); + $this->connectionId = $connectionId; + $this->record = array(); + $this->resultId = $resultId; + $this->EOF = false; + } + + /** + * Frees resultset + * + * @access public + */ + + function close() + { + pg_free_result( $this->resultId ); + $this->fields = array(); + $this->resultId = false; + } + + /** + * Returns field name from select query + * + * @access public + * @param string $field + * @return string Field name + */ + + function fields( $field ) + { + if(empty($field)) + { + return $this->fields; + } + else + { + return $this->fields[$field]; + } + } + + /** + * Returns numrows from select query + * + * @access public + * @return integer Numrows + */ + + function RecordCount() + { + return $this->_numOfRows; + } + + /** + * Returns num of fields from select query + * + * @access public + * @return integer numfields + */ + + function FieldCount() + { + return $this->_numOfFields; + } + + /** + * Returns next record + * + * @access public + */ + + function MoveNext() + { + if (@$this->fields = pg_fetch_array($this->resultId, NULL, $this->fetchMode)) { + $this->_currentRow += 1; + return true; + } + if (!$this->EOF) { + $this->_currentRow += 1; + $this->EOF = true; + } + return false; + } + + /** + * Move to the first row in the recordset. Many databases do NOT support this. + * + * @return true or false + */ + + function MoveFirst() + { + if ($this->_currentRow == 0) return true; + return $this->Move(0); + } + + /** + * Returns the Last Record + * + * @access public + */ + + function MoveLast() + { + if ($this->EOF) return false; + return $this->Move($this->_numOfRows - 1); + } + + /** + * Random access to a specific row in the recordset. Some databases do not support + * access to previous rows in the databases (no scrolling backwards). + * + * @param rowNumber is the row to move to (0-based) + * + * @return true if there still rows available, or false if there are no more rows (EOF). + */ + + function Move($rowNumber = 0) + { + if ($rowNumber == $this->_currentRow) return true; + $this->EOF = false; + if ($this->_numOfRows > 0){ + if ($rowNumber >= $this->_numOfRows - 1){ + $rowNumber = $this->_numOfRows - 1; + } + } + + if ($this->_seek($rowNumber)) { + $this->_currentRow = $rowNumber; + if ($this->_fetch()) { + return true; + } + $this->fields = false; + } + $this->EOF = true; + return false; + } + + /** + * Perform Seek to specific row + * + * @access private + */ + + function _seek($row) + { + if ($this->_numOfRows == 0) return false; + return @pg_result_seek($this->resultId,$row); + } + + /** + * Fills field array with first database element when query initially executed + * + * @access private + */ + + function _fetch() + { + $this->fields = @pg_fetch_array($this->resultId, NULL, $this->fetchMode); + return is_array($this->fields); + } + + /** + * Check to see if last record reached + * + * @access public + */ + + function EOF() + { + if( $this->_currentRow < $this->_numOfRows) + { + return false; + } + else + { + $this->EOF = true; + return true; + } + } + + /** + * Returns All Records in an array + * + * @access public + * @param [nRows] is the number of rows to return. -1 means every row. + */ + + function &GetArray($nRows = -1) + { + $results = array(); + $cnt = 0; + while (!$this->EOF && $nRows != $cnt) { + $results[] = $this->fields; + $this->MoveNext(); + $cnt++; + } + return $results; + } + + function &GetRows($nRows = -1) + { + $arr =& $this->GetArray($nRows); + return $arr; + } + + function &GetAll($nRows = -1) + { + $arr =& $this->GetArray($nRows); + return $arr; + } + + /** + * Fetch field information for a table. + * + * @return object containing the name, type and max_length + */ + function FetchField($fieldOffset = -1) + { + $fieldObject= new ADOFieldObject(); + $fieldObject->name = @pg_fieldname($this->resultId, $fieldOffset); + $fieldObject->type = @pg_fieldtype($this->resultId, $fieldOffset); + $fieldObject->max_length = @pg_fieldsize($this->resultId, $fieldOffset); + return $fieldObject; + } +} +?> \ No newline at end of file diff --git a/lib/adodb/adodbSQL_drivers/postgres8/postgres8_extend_module.inc b/lib/adodb/adodbSQL_drivers/postgres8/postgres8_extend_module.inc new file mode 100644 index 0000000..c1096dc --- /dev/null +++ b/lib/adodb/adodbSQL_drivers/postgres8/postgres8_extend_module.inc @@ -0,0 +1,147 @@ +Execute($sql, $inputarr); + if ($result) { + $data =& $result->GetAssoc($force_array, $first2cols); + $result->Close(); + } + return $data; + } + + /** + * Generates a sequence id and stores it in $this->genID; + * GenID is only available if $this->hasGenID = true; + * + * @param seqname name of sequence to use + * @param startID if sequence does not exist, start at this ID + * @return 0 if not supported, otherwise a sequence id + */ + + var $_genIDSQL = "SELECT NEXTVAL('%s')"; + var $_genSeqSQL = "CREATE SEQUENCE %s START %s"; + var $_dropSeqSQL = "DROP SEQUENCE %s"; + var $genID = 0; + + function GenID($seqname='adodbseq', $startID=1) + { + $getnext = sprintf($this->_genIDSQL, $seqname); + $holdtransOK = $this->transaction_status; + $save_handler = $this->raiseErrorFn; + $this->raiseErrorFn = ''; + @($result = $this->Execute($getnext)); + $this->raiseErrorFn = $save_handler; + + if (!$result) { + $this->transaction_status = $holdtransOK; + $createseq = $this->Execute(sprintf($this->_genSeqSQL, $seqname, $startID)); + $result = $this->Execute($getnext); + } + if ($result && !$result->EOF) + $this->genID = reset($result->fields); + else $this->genID = 0; + + if ($result) + $result->Close(); + + return $this->genID; + } + + function CreateSequence($seqname='adodbseq', $startID=1) + { + return $this->Execute(sprintf($this->_genSeqSQL, $seqname, $startID)); + } + + function DropSequence($seqname='adodbseq') + { + return $this->Execute(sprintf($this->_dropSeqSQL, $seqname)); + } + +} + +eval('class postgres8_extend_resultset_EXTENDER extends '. $last_module . '_ResultSet { }'); + +class postgres8_extend_ResultSet extends postgres8_extend_resultset_EXTENDER +{ + function &GetAssoc($force_array = false, $first2cols = false) + { + $results = false; + + if ($this->_numOfFields > 1) { + $numIndex = isset($this->fields[0]); + $results = array(); + if (!$first2cols && ($this->_numOfFields > 2 || $force_array)) { + if ($numIndex) { + while (!$this->EOF) { + $results[trim($this->fields[0])] = array_slice($this->fields, 1); + $this->MoveNext(); + } + } else { + while (!$this->EOF) { + $results[trim(reset($this->fields))] = array_slice($this->fields, 1); + $this->MoveNext(); + } + } + } else { + if ($numIndex) { + while (!$this->EOF) { + $results[trim(($this->fields[0]))] = $this->fields[1]; + $this->MoveNext(); + } + } else { + while (!$this->EOF) { + $v1 = trim(reset($this->fields)); + $v2 = ''.next($this->fields); + $results[$v1] = $v2; + $this->MoveNext(); + } + } + } + } + return $results; + } + + function PO_RecordCount($table="", $condition="") + { + $lnumrows = $this->_numOfRows; + if($lnumrows == -1 && $this->connectionId) + { + if($table) + { + if ($condition) + $condition = " WHERE " . $condition; + $resultrows = &$this->connectionId->Execute("SELECT COUNT(*) FROM $table $condition"); + if ($resultrows) + $lnumrows = reset($resultrows->fields); + } + } + return $lnumrows; + } + + function CurrentRow() + { + return $this->_currentRow; + } + + function AbsolutePosition() + { + return $this->_currentRow; + } + + function NextRecordSet() + { + return false; + } + +} +?> \ No newline at end of file diff --git a/lib/adodb/adodbSQL_drivers/postgres8/postgres8_meta_module.inc b/lib/adodb/adodbSQL_drivers/postgres8/postgres8_meta_module.inc new file mode 100644 index 0000000..87d9a72 --- /dev/null +++ b/lib/adodb/adodbSQL_drivers/postgres8/postgres8_meta_module.inc @@ -0,0 +1,712 @@ + 0 AND a.atttypid = t.oid AND a.attrelid = c.oid ORDER BY a.attnum"; + + // used when schema defined + var $metaColumnsSQL1 = "SELECT a.attname, t.typname, a.attlen, a.atttypmod, a.attnotnull, a.atthasdef, a.attnum +FROM pg_class c, pg_attribute a, pg_type t, pg_namespace n +WHERE relkind in ('r','v') AND (c.relname='%s' or c.relname = lower('%s')) + and c.relnamespace=n.oid and n.nspname='%s' + and a.attname not like '....%%' AND a.attnum > 0 + AND a.atttypid = t.oid AND a.attrelid = c.oid ORDER BY a.attnum"; + + // get primary key etc -- from Freek Dijkstra + var $metaKeySQL = "SELECT ic.relname AS index_name, a.attname AS column_name,i.indisunique AS unique_key, i.indisprimary AS primary_key + FROM pg_class bc, pg_class ic, pg_index i, pg_attribute a WHERE bc.oid = i.indrelid AND ic.oid = i.indexrelid AND (i.indkey[0] = a.attnum OR i.indkey[1] = a.attnum OR i.indkey[2] = a.attnum OR i.indkey[3] = a.attnum OR i.indkey[4] = a.attnum OR i.indkey[5] = a.attnum OR i.indkey[6] = a.attnum OR i.indkey[7] = a.attnum) AND a.attrelid = bc.oid AND bc.relname = '%s'"; + var $metaDefaultsSQL = "SELECT d.adnum as num, d.adsrc as def from pg_attrdef d, pg_class c where d.adrelid=c.oid and c.relname='%s' order by d.adnum"; + + function MetaError($err=false) + { + include_once(ADODB_DIR."/adodb-error.inc.php"); + if ($err === false) + $err = $this->ErrorNo(); + + return adodb_error($this->dataProvider,$this->databaseType,$err); + } + + function MetaErrorMsg($errno) + { + include_once(ADODB_DIR."/adodb-error.inc.php"); + return adodb_errormsg($errno); + } + + /** + * @returns an array with the primary key columns in it. + */ + function MetaPrimaryKeys($table, $owner=false) + { + // owner not used in base class - see oci8 + $p = array(); + $objs =& $this->MetaColumns($table); + if ($objs) { + foreach($objs as $v) { + if (!empty($v->primary_key)) + $p[] = $v->name; + } + } + if (sizeof($p)) return $p; + if (function_exists('ADODB_VIEW_PRIMARYKEYS')) + return ADODB_VIEW_PRIMARYKEYS($this->databaseType, $this->database, $table, $owner); + return false; + } + + /** + * @returns assoc array where keys are tables, and values are foreign keys + */ + function MetaForeignKeys($table, $owner=false, $upper=false) + { + $sql = 'SELECT t.tgargs as args + FROM + pg_trigger t,pg_class c,pg_proc p + WHERE + t.tgenabled AND + t.tgrelid = c.oid AND + t.tgfoid = p.oid AND + p.proname = \'RI_FKey_check_ins\' AND + c.relname = \''.strtolower($table).'\' + ORDER BY + t.tgrelid'; + + $rs =& $this->Execute($sql); + + if (!$rs || $rs->EOF) return false; + + $arr =& $rs->GetArray(); + $a = array(); + foreach($arr as $v) { + $data = explode(chr(0), $v['args']); + $size = count($data)-1; //-1 because the last node is empty + for($i = 4; $i < $size; $i++) { + if ($upper) + $a[strtoupper($data[2])][] = strtoupper($data[$i].'='.$data[++$i]); + else + $a[$data[2]][] = $data[$i].'='.$data[++$i]; + } + } + return $a; + } + + // not the fastest implementation - quick and dirty - jlim + // for best performance, use the actual $rs->MetaType(). + function MetaType($t,$len=-1,$fieldobj=false) + { + if (empty($this->_metars)) { + $rsclass = $this->last_module_name . "_ResultSet"; + $this->_metars =& new $rsclass(false,$this->fetchMode); + } + + return $this->_metars->MetaType($t,$len,$fieldobj); + } + + /** + * return the databases that the driver can connect to. + * Some databases will return an empty array. + * + * @return an array of database names. + */ + function MetaDatabases() + { + global $ADODB_FETCH_MODE; + + if ($this->metaDatabasesSQL) { + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + + if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); + + $arr = $this->GetCol($this->metaDatabasesSQL); + if (isset($savem)) $this->SetFetchMode($savem); + $ADODB_FETCH_MODE = $save; + + return $arr; + } + + return false; + } + + /** + * @param ttype can either be 'VIEW' or 'TABLE' or false. + * If false, both views and tables are returned. + * "VIEW" returns only views + * "TABLE" returns only tables + * @param showSchema returns the schema/user with the table name, eg. USER.TABLE + * @param mask is the input mask - only supported by oci8 and postgresql + * + * @return array of tables for current database. + */ + function &MetaTables($ttype=false,$showSchema=false,$mask=false) + { + $info = $this->ServerInfo(); + if ($info['version'] >= 7.3) { + $this->metaTablesSQL = "select tablename,'T' from pg_tables where tablename not like 'pg\_%' + and schemaname not in ( 'pg_catalog','information_schema') + union + select viewname,'V' from pg_views where viewname not like 'pg\_%' and schemaname not in ( 'pg_catalog','information_schema') "; + } + if ($mask) { + $save = $this->metaTablesSQL; + $mask = $this->qstr(strtolower($mask)); + if ($info['version']>=7.3) + $this->metaTablesSQL = " +select tablename,'T' from pg_tables where tablename like $mask and schemaname not in ( 'pg_catalog','information_schema') + union +select viewname,'V' from pg_views where viewname like $mask and schemaname not in ( 'pg_catalog','information_schema') "; + else + $this->metaTablesSQL = " +select tablename,'T' from pg_tables where tablename like $mask + union +select viewname,'V' from pg_views where viewname like $mask"; + } + $ret =& $this->_MetaTables($ttype,$showSchema); + + if ($mask) { + $this->metaTablesSQL = $save; + } + return $ret; + } + + function &_MetaTables($ttype=false,$showSchema=false,$mask=false) + { + global $ADODB_FETCH_MODE; + + $false = false; + if ($mask) { + return $false; + } + if ($this->metaTablesSQL) { + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + + if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); + + $rs = $this->Execute($this->metaTablesSQL); + if (isset($savem)) $this->SetFetchMode($savem); + $ADODB_FETCH_MODE = $save; + + if ($rs === false) return $false; + $arr =& $rs->GetArray(); + $arr2 = array(); + + if ($hast = ($ttype && isset($arr[0][1]))) { + $showt = strncmp($ttype,'T',1); + } + + for ($i=0; $i < sizeof($arr); $i++) { + if ($hast) { + if ($showt == 0) { + if (strncmp($arr[$i][1],'T',1) == 0) $arr2[] = trim($arr[$i][0]); + } else { + if (strncmp($arr[$i][1],'V',1) == 0) $arr2[] = trim($arr[$i][0]); + } + } else + $arr2[] = trim($arr[$i][0]); + } + $rs->Close(); + return $arr2; + } + return $false; + } + + function _findschema(&$table,&$schema) + { + if (!$schema && ($at = strpos($table,'.')) !== false) { + $schema = substr($table,0,$at); + $table = substr($table,$at+1); + } + } + + /** + * List columns in a database as an array of ADOFieldObjects. + * See top of file for definition of object. + * + * @param $table table name to query + * @param $normalize makes table name case-insensitive (required by some databases) + * @schema is optional database schema to use - not supported by all databases. + * + * @return array of ADOFieldObjects for current table. + */ + function &MetaColumns($table,$normalize=true) + { + global $ADODB_FETCH_MODE; + + $schema = false; + $false = false; + $this->_findschema($table,$schema); + + if ($normalize) $table = strtolower($table); + + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); + + if ($schema) $rs =& $this->Execute(sprintf($this->metaColumnsSQL1,$table,$table,$schema)); + else $rs =& $this->Execute(sprintf($this->metaColumnsSQL,$table,$table)); + if (isset($savem)) $this->SetFetchMode($savem); + $ADODB_FETCH_MODE = $save; + + if ($rs === false) { + return $false; + } + if (!empty($this->metaKeySQL)) { + // If we want the primary keys, we have to issue a separate query + // Of course, a modified version of the metaColumnsSQL query using a + // LEFT JOIN would have been much more elegant, but postgres does + // not support OUTER JOINS. So here is the clumsy way. + + $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; + + $rskey = $this->Execute(sprintf($this->metaKeySQL,($table))); + // fetch all result in once for performance. + $keys =& $rskey->GetArray(); + if (isset($savem)) $this->SetFetchMode($savem); + $ADODB_FETCH_MODE = $save; + + $rskey->Close(); + unset($rskey); + } + + $rsdefa = array(); + if (!empty($this->metaDefaultsSQL)) { + $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; + $sql = sprintf($this->metaDefaultsSQL, ($table)); + $rsdef = $this->Execute($sql); + if (isset($savem)) $this->SetFetchMode($savem); + $ADODB_FETCH_MODE = $save; + + if ($rsdef) { + while (!$rsdef->EOF) { + $num = $rsdef->fields['num']; + $s = $rsdef->fields['def']; + if (strpos($s,'::')===false && substr($s, 0, 1) == "'") { /* quoted strings hack... for now... fixme */ + $s = substr($s, 1); + $s = substr($s, 0, strlen($s) - 1); + } + + $rsdefa[$num] = $s; + $rsdef->MoveNext(); + } + } else { + ADOConnection::outp( "==> SQL => " . $sql); + } + unset($rsdef); + } + + $retarr = array(); + while (!$rs->EOF) { + $fld = new ADOFieldObject(); + $fld->name = $rs->fields[0]; + $fld->type = $rs->fields[1]; + $fld->max_length = $rs->fields[2]; + $fld->attnum = $rs->fields[6]; + + if ($fld->max_length <= 0) $fld->max_length = $rs->fields[3]-4; + if ($fld->max_length <= 0) $fld->max_length = -1; + if ($fld->type == 'numeric') { + $fld->scale = $fld->max_length & 0xFFFF; + $fld->max_length >>= 16; + } + // dannym + // 5 hasdefault; 6 num-of-column + $fld->has_default = ($rs->fields[5] == 't'); + if ($fld->has_default) { + $fld->default_value = $rsdefa[$rs->fields[6]]; + } + + //Freek + $fld->not_null = $rs->fields[4] == 't'; + + + // Freek + if (is_array($keys)) { + foreach($keys as $key) { + if ($fld->name == $key['column_name'] AND $key['primary_key'] == 't') + $fld->primary_key = true; + if ($fld->name == $key['column_name'] AND $key['unique_key'] == 't') + $fld->unique = true; // What name is more compatible? + } + } + + if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld; + else $retarr[($normalize) ? strtoupper($fld->name) : $fld->name] = $fld; + + $rs->MoveNext(); + } + $rs->Close(); + if (empty($retarr)) + return $false; + else + return $retarr; + + } + + /** + * List indexes on a table as an array. + * @param table table name to query + * @param primary true to only show primary keys. Not actually used for most databases + * + * @return array of indexes on current table. Each element represents an index, and is itself an associative array. + + Array ( + [name_of_index] => Array + ( + [unique] => true or false + [columns] => Array + ( + [0] => firstname + [1] => lastname + ) + ) + */ + function &MetaIndexes ($table, $primary = FALSE) + { + global $ADODB_FETCH_MODE; + + $schema = false; + $this->_findschema($table,$schema); + + if ($schema) { // requires pgsql 7.3+ - pg_namespace used. + $sql = ' +SELECT c.relname as "Name", i.indisunique as "Unique", i.indkey as "Columns" +FROM pg_catalog.pg_class c +JOIN pg_catalog.pg_index i ON i.indexrelid=c.oid +JOIN pg_catalog.pg_class c2 ON c2.oid=i.indrelid + ,pg_namespace n +WHERE (c2.relname=\'%s\' or c2.relname=lower(\'%s\')) and c.relnamespace=c2.relnamespace and c.relnamespace=n.oid and n.nspname=\'%s\''; + } else { + $sql = ' +SELECT c.relname as "Name", i.indisunique as "Unique", i.indkey as "Columns" +FROM pg_catalog.pg_class c +JOIN pg_catalog.pg_index i ON i.indexrelid=c.oid +JOIN pg_catalog.pg_class c2 ON c2.oid=i.indrelid +WHERE (c2.relname=\'%s\' or c2.relname=lower(\'%s\'))'; + } + + if ($primary == FALSE) { + $sql .= ' AND i.indisprimary=false;'; + } + + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + if ($this->fetchMode !== FALSE) { + $savem = $this->SetFetchMode(FALSE); + } + + $rs = $this->Execute(sprintf($sql,$table,$table,$schema)); + if (isset($savem)) { + $this->SetFetchMode($savem); + } + $ADODB_FETCH_MODE = $save; + + if (!is_object($rs)) { + $false = false; + return $false; + } + + $col_names = $this->MetaColumnNames($table,true,true); + //3rd param is use attnum, + // see http://sourceforge.net/tracker/index.php?func=detail&aid=1451245&group_id=42718&atid=433976 + $indexes = array(); + while ($row = $rs->FetchRow()) { + $columns = array(); + foreach (explode(' ', $row[2]) as $col) { + $columns[] = $col_names[$col]; + } + + $indexes[$row[0]] = array( + 'unique' => ($row[1] == 't'), + 'columns' => $columns + ); + } + return $indexes; + } + + /** + * List columns names in a table as an array. + * @param table table name to query + * + * @return array of column names for current table. + */ + function &MetaColumnNames($table, $numIndexes=false,$useattnum=false /* only for postgres */) + { + $objarr =& $this->MetaColumns($table); + if (!is_array($objarr)) { + $false = false; + return $false; + } + $arr = array(); + if ($numIndexes) { + $i = 0; + if ($useattnum) { + foreach($objarr as $v) + $arr[$v->attnum] = $v->name; + + } else + foreach($objarr as $v) $arr[$i++] = $v->name; + } else + foreach($objarr as $v) $arr[strtoupper($v->name)] = $v->name; + + return $arr; + } + + function MetaTransaction($mode,$db) + { + $mode = strtoupper($mode); + $mode = str_replace('ISOLATION LEVEL ','',$mode); + + switch($mode) { + + case 'READ UNCOMMITTED': + switch($db) { + case 'oci8': + case 'oracle': + return 'ISOLATION LEVEL READ COMMITTED'; + default: + return 'ISOLATION LEVEL READ UNCOMMITTED'; + } + break; + + case 'READ COMMITTED': + return 'ISOLATION LEVEL READ COMMITTED'; + break; + + case 'REPEATABLE READ': + switch($db) { + case 'oci8': + case 'oracle': + return 'ISOLATION LEVEL SERIALIZABLE'; + default: + return 'ISOLATION LEVEL REPEATABLE READ'; + } + break; + + case 'SERIALIZABLE': + return 'ISOLATION LEVEL SERIALIZABLE'; + break; + + default: + return $mode; + } + } + +} + +eval('class postgres8_meta_resultset_EXTENDER extends '. $last_module . '_ResultSet { }'); + +class postgres8_meta_ResultSet extends postgres8_meta_resultset_EXTENDER +{ + /** + * Get the metatype of the column. This is used for formatting. This is because + * many databases use different names for the same type, so we transform the original + * type to our standardised version which uses 1 character codes: + * + * @param t is the type passed in. Normally is ADOFieldObject->type. + * @param len is the maximum length of that field. This is because we treat character + * fields bigger than a certain size as a 'B' (blob). + * @param fieldobj is the field object returned by the database driver. Can hold + * additional info (eg. primary_key for mysql). + * + * @return the general type of the data: + * C for character < 250 chars + * X for teXt (>= 250 chars) + * B for Binary + * N for numeric or floating point + * D for date + * T for timestamp + * L for logical/Boolean + * I for integer + * R for autoincrement counter/integer + * + * + */ + function MetaType($t,$len=-1,$fieldobj=false) + { + if (is_object($t)) { + $fieldobj = $t; + $t = $fieldobj->type; + $len = $fieldobj->max_length; + } + $is_serial = is_object($fieldobj) && $fieldobj->primary_key && $fieldobj->unique && $fieldobj->has_default && substr($fieldobj->default_value,0,8) == 'nextval('; + switch (strtoupper($t)) { + case 'MONEY': // stupid, postgres expects money to be a string + case 'INTERVAL': + case 'CHAR': + case 'CHARACTER': + case 'VARCHAR': + case 'NAME': + case 'BPCHAR': + case '_VARCHAR': + case 'INET': + case 'MACADDR': + if ($len <= $this->blobSize) return 'C'; + case 'TEXT': + return 'X'; + case 'IMAGE': // user defined type + case 'BLOB': // user defined type + case 'BIT': // This is a bit string, not a single bit, so don't return 'L' + case 'VARBIT': + case 'BYTEA': + return 'B'; + case 'BOOL': + case 'BOOLEAN': + return 'L'; + case 'DATE': + return 'D'; + case 'TIMESTAMP WITHOUT TIME ZONE': + case 'TIME': + case 'DATETIME': + case 'TIMESTAMP': + case 'TIMESTAMPTZ': + return 'T'; + case 'INTEGER': return !$is_serial ? 'I' : 'R'; + case 'SMALLINT': + case 'INT2': return !$is_serial ? 'I2' : 'R'; + case 'INT4': return !$is_serial ? 'I4' : 'R'; + case 'BIGINT': + case 'INT8': return !$is_serial ? 'I8' : 'R'; + case 'OID': + case 'SERIAL': + return 'R'; + case 'FLOAT4': + case 'FLOAT8': + case 'DOUBLE PRECISION': + case 'REAL': + return 'F'; + default: + static $typeMap = array( + 'VARCHAR' => 'C', + 'VARCHAR2' => 'C', + 'CHAR' => 'C', + 'C' => 'C', + 'STRING' => 'C', + 'NCHAR' => 'C', + 'NVARCHAR' => 'C', + 'VARYING' => 'C', + 'BPCHAR' => 'C', + 'CHARACTER' => 'C', + 'INTERVAL' => 'C', # Postgres + 'MACADDR' => 'C', # postgres + ## + 'LONGCHAR' => 'X', + 'TEXT' => 'X', + 'NTEXT' => 'X', + 'M' => 'X', + 'X' => 'X', + 'CLOB' => 'X', + 'NCLOB' => 'X', + 'LVARCHAR' => 'X', + ## + 'BLOB' => 'B', + 'IMAGE' => 'B', + 'BINARY' => 'B', + 'VARBINARY' => 'B', + 'LONGBINARY' => 'B', + 'B' => 'B', + ## + 'YEAR' => 'D', // mysql + 'DATE' => 'D', + 'D' => 'D', + ## + 'UNIQUEIDENTIFIER' => 'C', # MS SQL Server + ## + 'TIME' => 'T', + 'TIMESTAMP' => 'T', + 'DATETIME' => 'T', + 'TIMESTAMPTZ' => 'T', + 'T' => 'T', + 'TIMESTAMP WITHOUT TIME ZONE' => 'T', // postgresql + ## + 'BOOL' => 'L', + 'BOOLEAN' => 'L', + 'BIT' => 'L', + 'L' => 'L', + ## + 'COUNTER' => 'R', + 'R' => 'R', + 'SERIAL' => 'R', // ifx + 'INT IDENTITY' => 'R', + ## + 'INT' => 'I', + 'INT2' => 'I', + 'INT4' => 'I', + 'INT8' => 'I', + 'INTEGER' => 'I', + 'INTEGER UNSIGNED' => 'I', + 'SHORT' => 'I', + 'TINYINT' => 'I', + 'SMALLINT' => 'I', + 'I' => 'I', + ## + 'LONG' => 'N', // interbase is numeric, oci8 is blob + 'BIGINT' => 'N', // this is bigger than PHP 32-bit integers + 'DECIMAL' => 'N', + 'DEC' => 'N', + 'REAL' => 'N', + 'DOUBLE' => 'N', + 'DOUBLE PRECISION' => 'N', + 'SMALLFLOAT' => 'N', + 'FLOAT' => 'N', + 'NUMBER' => 'N', + 'NUM' => 'N', + 'NUMERIC' => 'N', + 'MONEY' => 'N', + + ## informix 9.2 + 'SQLINT' => 'I', + 'SQLSERIAL' => 'I', + 'SQLSMINT' => 'I', + 'SQLSMFLOAT' => 'N', + 'SQLFLOAT' => 'N', + 'SQLMONEY' => 'N', + 'SQLDECIMAL' => 'N', + 'SQLDATE' => 'D', + 'SQLVCHAR' => 'C', + 'SQLCHAR' => 'C', + 'SQLDTIME' => 'T', + 'SQLINTERVAL' => 'N', + 'SQLBYTES' => 'B', + 'SQLTEXT' => 'X', + ## informix 10 + "SQLINT8" => 'I8', + "SQLSERIAL8" => 'I8', + "SQLNCHAR" => 'C', + "SQLNVCHAR" => 'C', + "SQLLVARCHAR" => 'X', + "SQLBOOL" => 'L' + ); + + $tmap = false; + $t = strtoupper($t); + $tmap = (isset($typeMap[$t])) ? $typeMap[$t] : 'N'; + if ($t == 'LONG' && $this->dataProvider == 'oci8') return 'B'; + return $tmap; + } + } + +} + +?> \ No newline at end of file diff --git a/lib/adodb/adodbSQL_drivers/postgres8/postgres8_transaction_module.inc b/lib/adodb/adodbSQL_drivers/postgres8/postgres8_transaction_module.inc new file mode 100644 index 0000000..94bc45e --- /dev/null +++ b/lib/adodb/adodbSQL_drivers/postgres8/postgres8_transaction_module.inc @@ -0,0 +1,128 @@ +transOff > 0) { + $this->transOff += 1; + return; + } + $this->transaction_status = true; + + if ($this->debug && $this->transCnt > 0) + ADOConnection::outp("Bad Transaction: StartTrans called within BeginTrans"); + + $this->BeginTrans(); + $this->transOff = 1; + } + + function BeginTrans() + { + if ($this->transOff) + return true; + + $this->transCnt += 1; + return @pg_Exec($this->connectionId, "begin"); + } + + function CompleteTrans($autoComplete = true) + { + if ($this->transOff > 1) { + $this->transOff -= 1; + return true; + } + $this->transOff = 0; + if ($this->transaction_status && $autoComplete) { + if (!$this->CommitTrans()) { + $this->transaction_status = false; + if ($this->debug) + ADOConnection::outp("Smart Commit failed"); + } else + if ($this->debug) + ADOConnection::outp("Smart Commit occurred"); + } else { + $this->RollbackTrans(); + if ($this->debug) + ADOCOnnection::outp("Smart Rollback occurred"); + } + return $this->transaction_status; + } + + function CommitTrans($ok=true) + { + if ($this->transOff) + return true; + + if (!$ok) + return $this->RollbackTrans(); + + $this->transCnt -= 1; + return @pg_Exec($this->connectionId, "commit"); + } + + function RollbackTrans() + { + if ($this->transOff) + return true; + + $this->transCnt -= 1; + return @pg_Exec($this->connectionId, "rollback"); + } + + function FailTrans() + { + if ($this->debug) + if ($this->transOff == 0) { + ADOConnection::outp("FailTrans outside StartTrans/CompleteTrans"); + } else { + ADOConnection::outp("FailTrans was called"); + } + $this->transaction_status = false; + } + + function HasFailedTrans() + { + if ($this->transOff > 0) + return $this->transaction_status == false; + + return false; + } + + function RowLock($tables,$where,$flds='1 as ignore') + { + if (!$this->transCnt) + $this->BeginTrans(); + + return $this->GetOne("select $flds from $tables where $where for update"); + } + + function CommitLock($table) + { + return $this->CommitTrans(); + } + + function RollbackLock($table) + { + return $this->RollbackTrans(); + } + +} + +eval('class postgres8_transaction_resultset_EXTENDER extends '. $last_module . '_ResultSet { }'); + +class postgres8_transaction_ResultSet extends postgres8_transaction_resultset_EXTENDER +{ +} +?> \ No newline at end of file diff --git a/lib/adodb/adodbSQL_drivers/sqlite/sqlite_datadict.inc b/lib/adodb/adodbSQL_drivers/sqlite/sqlite_datadict.inc new file mode 100644 index 0000000..25fb13e --- /dev/null +++ b/lib/adodb/adodbSQL_drivers/sqlite/sqlite_datadict.inc @@ -0,0 +1,67 @@ +debug) $this->outp("AlterColumnSQL not supported"); + return array(); + } + + function DropColumnSQL($tabname, $flds) + { + if ($this->debug) $this->outp("DropColumnSQL not supported"); + return array(); + } + +} + +?> \ No newline at end of file diff --git a/lib/adodb/adodbSQL_drivers/sqlite/sqlite_date_module.inc b/lib/adodb/adodbSQL_drivers/sqlite/sqlite_date_module.inc new file mode 100644 index 0000000..bcf36dc --- /dev/null +++ b/lib/adodb/adodbSQL_drivers/sqlite/sqlite_date_module.inc @@ -0,0 +1,241 @@ +_Execute("select $this->sysTimeStamp"); + if ($rs && !$rs->EOF) + return $this->UnixTimeStamp(reset($rs->fields)); + + return false; + } + + function OffsetDate($dayFraction, $date=false) + { + if (!$date) + $date = $this->sysDate; + + return '('.$date.'+'.$dayFraction.')'; + } + + function SetDateLocale($locale = 'En') + { + $this->locale = $locale; + switch (strtoupper($locale)) + { + case 'EN': + $this->fmtDate="'Y-m-d'"; + $this->fmtTimeStamp = "'Y-m-d H:i:s'"; + break; + + case 'US': + $this->fmtDate = "'m-d-Y'"; + $this->fmtTimeStamp = "'m-d-Y H:i:s'"; + break; + + case 'NL': + case 'FR': + case 'RO': + case 'IT': + $this->fmtDate="'d-m-Y'"; + $this->fmtTimeStamp = "'d-m-Y H:i:s'"; + break; + + case 'GE': + $this->fmtDate="'d.m.Y'"; + $this->fmtTimeStamp = "'d.m.Y H:i:s'"; + break; + + default: + $this->fmtDate="'Y-m-d'"; + $this->fmtTimeStamp = "'Y-m-d H:i:s'"; + break; + } + } + + function DBDate($date) + { + if (empty($date) && $date !== 0) + return 'null'; + + if (is_string($date) && !is_numeric($date)) { + if ($date === 'null' || strncmp($date, "'", 1) === 0) + return $date; + + if ($this->isoDates) + return "'$date'"; + + $date = $this->UnixDate($date); + } + + return adodb_date($this->fmtDate,$date); + } + + function DBTimeStamp($timestamp) + { + if (empty($timestamp) && $timestamp !== 0) + return 'null'; + + # strlen(14) allows YYYYMMDDHHMMSS format + if (!is_string($timestamp) || (is_numeric($timestamp) && strlen($timestamp)<14)) + return adodb_date($this->fmtTimeStamp, $timestamp); + + if ($timestamp === 'null') + return $timestamp; + + if ($this->isoDates && strlen($timestamp) !== 14) + return "'$timestamp'"; + + $timestamp = $this->UnixTimeStamp($timestamp); + return adodb_date($this->fmtTimeStamp, $timestamp); + } + + function UnixDate($v) + { + if (is_object($v)) { + // odbtp support + //( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 ) + return adodb_mktime($v->hour, $v->minute, $v->second, $v->month, $v->day, $v->year); + } + + if (is_numeric($v) && strlen($v) !== 8) + return $v; + + if (!preg_match( "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})|", ($v), $rr)) + return false; + + if ($rr[1] <= TIMESTAMP_FIRST_YEAR) + return 0; + + // h-m-s-MM-DD-YY + return adodb_mktime(0, 0, 0, $rr[2], $rr[3], $rr[1]); + } + + function UnixTimeStamp($v) + { + if (is_object($v)) { + // odbtp support + //( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 ) + return adodb_mktime($v->hour, $v->minute, $v->second, $v->month, $v->day, $v->year); + } + + if (!preg_match("|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ ,-]*(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,4}))?|", ($v), $rr)) + return false; + + if ($rr[1] <= TIMESTAMP_FIRST_YEAR && $rr[2]<= 1) + return 0; + + // h-m-s-MM-DD-YY + if (!isset($rr[5])) + return adodb_mktime(0, 0, 0, $rr[2], $rr[3], $rr[1]); + else return adodb_mktime($rr[5], $rr[6], $rr[7], $rr[2], $rr[3], $rr[1]); + } + + function UserDate($v, $fmt='Y-m-d', $gmt=false) + { + $tt = $this->UnixDate($v); + + // $tt == -1 if pre TIMESTAMP_FIRST_YEAR + if (($tt === false || $tt == -1) && $v != false) + return $v; + else if ($tt == 0) + return $this->emptyDate; + else if ($tt == -1) { // pre-TIMESTAMP_FIRST_YEAR + } + + return ($gmt) ? adodb_gmdate($fmt, $tt) : adodb_date($fmt, $tt); + } + + function UserTimeStamp($v, $fmt='Y-m-d H:i:s', $gmt=false) + { + if (!isset($v)) + return $this->emptyTimeStamp; + + # strlen(14) allows YYYYMMDDHHMMSS format + if (is_numeric($v) && strlen($v)<14) + return ($gmt) ? adodb_gmdate($fmt,$v) : adodb_date($fmt,$v); + + $tt = $this->UnixTimeStamp($v); + // $tt == -1 if pre TIMESTAMP_FIRST_YEAR + if (($tt === false || $tt == -1) && $v != false) + return $v; + + if ($tt == 0) + return $this->emptyTimeStamp; + else return ($gmt) ? adodb_gmdate($fmt,$tt) : adodb_date($fmt,$tt); + } + + function SQLDate($fmt, $col=false) + { + $fmt = $this->qstr($fmt); + return ($col) ? "adodb_date2($fmt,$col)" : "adodb_date($fmt)"; + } +} + +eval('class sqlite_date_resultset_EXTENDER extends '. $last_module . '_ResultSet { }'); + +class sqlite_date_ResultSet extends sqlite_date_resultset_EXTENDER +{ + var $emptyTimeStamp = ' '; /// what to display when $time==0 + var $emptyDate = ' '; /// what to display when $time==0 + var $datetime = false; + + function UserTimeStamp($v, $fmt='Y-m-d H:i:s') + { + if (is_numeric($v) && strlen($v)<14) + return adodb_date($fmt,$v); + + $tt = $this->UnixTimeStamp($v); + // $tt == -1 if pre TIMESTAMP_FIRST_YEAR + if (($tt === false || $tt == -1) && $v != false) + return $v; + + if ($tt === 0) + return $this->emptyTimeStamp; + else return adodb_date($fmt,$tt); + } + + function UserDate($v,$fmt='Y-m-d') + { + $tt = $this->UnixDate($v); + // $tt == -1 if pre TIMESTAMP_FIRST_YEAR + if (($tt === false || $tt == -1) && $v != false) + return $v; + else if ($tt == 0) + return $this->emptyDate; + else if ($tt == -1) { // pre-TIMESTAMP_FIRST_YEAR + } + return adodb_date($fmt,$tt); + } + + function UnixDate($v) + { + return sqlite_date_ADOConnection::UnixDate($v); + } + + function UnixTimeStamp($v) + { + return sqlite_date_ADOConnection::UnixTimeStamp($v); + } + +} +?> \ No newline at end of file diff --git a/lib/adodb/adodbSQL_drivers/sqlite/sqlite_driver.inc b/lib/adodb/adodbSQL_drivers/sqlite/sqlite_driver.inc new file mode 100644 index 0000000..21130fe --- /dev/null +++ b/lib/adodb/adodbSQL_drivers/sqlite/sqlite_driver.inc @@ -0,0 +1,614 @@ +dbtype = 'sqlite'; + $this->dataProvider = 'sqlite'; + } + + /** + * Connection to database server and selected database + * + * @access private + */ + + function _connect($host = "", $username = "", $password = "", $database = "", $persistent, $forcenew) + { + if (!function_exists('sqlite_factory')) return false; + + $this->host = $host; + $this->username = $username; + $this->password = $password; + $this->database = ($database == "") ? $host : $database; + $this->persistent = $persistent; + $this->forcenewconnection = $forcenew; + + if($this->persistent == 1) + { + $this->connectionId = @sqlite_popen( $this->database ); + } + else + { + $this->connectionId = @sqlite_open( $this->database ); + } + + if ($this->connectionId === false) + { + if ($fn = $this->raiseErrorFn) + $fn($this->dbtype, 'CONNECT', $this->ErrorNo(), $this->ErrorMsg(), $this->host, $this->database, $this); + return false; + } + + return true; + } + + /** + * Choose a database to connect. + * + * @param dbname is the name of the database to select + * @return true or false + * @access public + */ + + function SelectDB($dbname) + { + $this->database = $dbname; + + $this->connectionId = sqlite_close( $this->connectionId ); + + if($this->persistent == 1) + { + $this->connectionId = @sqlite_popen( $this->database ); + } + else + { + $this->connectionId = @sqlite_open( $this->database ); + } + + if ($this->connectionId === false) + { + if ($fn = $this->raiseErrorFn) + $fn($this->dbtype, 'CONNECT', $this->ErrorNo(), $this->ErrorMsg(), $this->host, $this->database, $this); + return false; + } + + return true; + } + + /** + * Return database error message + * Usage: $errormessage =& $db->ErrorMsg(); + * + * @access public + */ + + function ErrorMsg() + { + $errorno = @sqlite_last_error( $this->connectionId ); + $error = ($errorno) ? sqlite_error_string($errorno) : ''; + return $error; + } + + /** + * Return database error number + * Usage: $errorbo =& $db->ErrorNo(); + * + * @access public + */ + + function ErrorNo() + { + return @sqlite_last_error($this->connectionId); + } + + /** + * Returns # of affected rows from insert/delete/update query + * + * @access public + * @return integer Affected rows + */ + + function Affected_Rows() + { + return @sqlite_changes( $this->connectionId ); + } + + /** + * Returns the last record id of an inserted item + * Usage: $db->Insert_ID(); + * + * @access public + */ + + function Insert_ID() + { + return @sqlite_last_insert_rowid($this->connectionId); + } + + /** + * Correctly quotes a string so that all strings are escape coded. + * An example is $db->qstr("Haven't a clue."); + * + * @param string the string to quote + * @param [magic_quotes] if $s is GET/POST var, set to get_magic_quotes_gpc(). + * + * @return single-quoted string IE: 'Haven\'t a clue.' + */ + + function qstr($string, $magic_quotes=false) + { + // Ugly hack alert! Repalce backticks with some arbitrary character + $string = str_replace('`', "\x1C", $string); + if (!$magic_quotes) { + return "'".str_replace("'", "''", $string)."'"; + } + $string = str_replace("\\'", "''", str_replace('\\\\', '\\', str_replace('\\"', '"', $string))); + return "'" . $string . "'"; + } + + function QMagic($string) + { + return $this->qstr($string, get_magic_quotes_gpc()); + } + + /** + * Returns concatenated string + * Usage: $db->Concat($str1,$str2); + * + * @return concatenated string + */ + function Concat() + { + $arr = func_get_args(); + return implode("||", $arr); + } + + function IfNull( $field, $ifNull ) + { + return " CASE WHEN $field is null THEN $ifNull ELSE $field END "; + } + + /** + * Closes database connection + * Usage: $db->close(); + * + * @access public + */ + + function Close() + { + @sqlite_close( $this->connectionId ); + $this->connectionId = false; + } + + /** + * Returns All Records in an array + * + * Usage: $db->GetAll($sql); + * @access public + */ + + function &GetAll($sql, $inputarr = false) + { + $data =& $this->GetArray($sql, $inputarr); + return $data; + } + + /** + * Returns All Records in an array + * + * Usage: $db->GetArray($sql); + * @access public + */ + + function &GetArray($sql, $inputarr = false) + { + $data = false; + $result =& $this->Execute($sql, $inputarr); + if ($result) + { + $data =& $result->GetArray(); + $result->Close(); + } + return $data; + } + + /** + * Executes SQL query and instantiates resultset methods + * + * @access private + * @return mixed Resultset methods + */ + + function &do_query( $sql, $offset, $nrows, $inputarr=false ) + { + global $ADODB_FETCH_MODE; + $sql = str_replace('`by`', "'by'", $sql); + $sql = str_replace('`order`', '"order"', $sql); + $sql = str_replace('`when`', '"when"', $sql); + $sql = str_replace('`desc`', "'desc'", $sql); + $sql = str_replace('`', '', $sql); + $sql = str_replace("\x1C", "`", $sql); + if (strpos($sql, "(DISTINCT ipmd5)")) { + $sql = str_replace("(DISTINCT ipmd5) FROM " . KU_DBPREFIX . "posts", "(ipmd5) FROM (SELECT DISTINCT ipmd5 FROM " . KU_DBPREFIX . "posts", $sql); + $sql .= ")"; + } + $sql = str_replace('IS_DELETED', 'is_deleted', $sql); + $sql = str_replace("HIGH_PRIORITY", "", $sql); + + $false = false; + + $offsetStr = ''; + $limitStr = ''; + if ($offset != -1 || $nrows != -1) + { + $offsetStr = ($offset>=0) ? " OFFSET " . $offset . "," : ''; + $limitStr = ($nrows >= 0) ? " LIMIT ". $nrows : ($offset >= 0 ? ' LIMIT 999999999' : ''); + } + + if ($inputarr && is_array($inputarr)) { + $sqlarr = explode('?', $sql); + if (!is_array(reset($inputarr))) $inputarr = array($inputarr); + foreach($inputarr as $arr) { + $sql = ''; $i = 0; + foreach($arr as $v) { + $sql .= $sqlarr[$i]; + switch(gettype($v)){ + case 'string': + $sql .= $this->qstr($v); + break; + case 'double': + $sql .= str_replace(',', '.', $v); + break; + case 'boolean': + $sql .= $v ? 1 : 0; + break; + default: + if ($v === null) + $sql .= 'NULL'; + else $sql .= $v; + } + $i += 1; + } + $sql .= $sqlarr[$i]; + if ($i+1 != sizeof($sqlarr)) + return $false; + $this->sql = $sql . "$limitStr$offsetStr"; + $time_start = array_sum(explode(' ', microtime())); + $this->query_count++; + $resultId = @sqlite_query( $this->sql, $this->connectionId ); + $time_total = (array_sum(explode(' ', microtime())) - $time_start); + $this->query_time_total += $time_total; + if($this->debug_console) + { + $this->query_list[] = $this->sql; + $this->query_list_time[] = $time_total; + $this->query_list_errors[] = $this->ErrorMsg(); + } + if($this->debug) + { + $this->outp($sql . "$limitStr$offsetStr"); + } + if ($resultId === false) { // error handling if query fails + if ($fn = $this->raiseErrorFn) + $fn($this->dbtype, 'EXECUTE', $this->ErrorNo(), $this->ErrorMsg(), $this->sql, $inputarr, $this); + return $false; + } + } + } + else + { + $this->sql = $sql . "$limitStr$offsetStr"; + $time_start = array_sum(explode(' ', microtime())); + $this->query_count++; + $resultId = @sqlite_query( $this->sql, $this->connectionId ); + $time_total = (array_sum(explode(' ', microtime())) - $time_start); + $this->query_time_total += $time_total; + if($this->debug_console) + { + $this->query_list[] = $this->sql; + $this->query_list_time[] = $time_total; + $this->query_list_errors[] = $this->ErrorMsg(); + } + if($this->debug) + { + $this->outp($sql . "$limitStr$offsetStr"); + } + } + + if ($resultId === false) { // error handling if query fails + if ($fn = $this->raiseErrorFn) + $fn($this->dbtype, 'EXECUTE', $this->ErrorNo(), $this->ErrorMsg(), $this->sql, $inputarr, $this); + return $false; + } + + if ($resultId === true) { // return simplified recordset for inserts/updates/deletes with lower overhead + $recordset = new ADORecordSet_empty(); + return $recordset; + } + + $resultset_name = $this->last_module_name . "_ResultSet"; + $recordset = new $resultset_name( $resultId, $this->connectionId ); + + $recordset->_currentRow = 0; + + switch ($ADODB_FETCH_MODE) + { + case ADODB_FETCH_NUM: $recordset->fetchMode = SQLITE_NUM; break; + case ADODB_FETCH_ASSOC:$recordset->fetchMode = SQLITE_ASSOC; break; + default: + case ADODB_FETCH_DEFAULT: + case ADODB_FETCH_BOTH:$recordset->fetchMode = SQLITE_BOTH; break; + } + + $recordset->_numOfRows = @sqlite_num_rows( $resultId ); + if( $recordset->_numOfRows == 0) + { + $recordset->EOF = true; + } + $recordset->_numOfFields = @sqlite_num_fields( $resultId ); + $recordset->_fetch(); + + return $recordset; + } +} + +class sqlite_driver_ResultSet +{ + var $connectionId; + var $fields; + var $resultId; + var $_currentRow = 0; + var $_numOfRows = -1; + var $_numOfFields = -1; + var $fetchMode; + var $EOF; + + /** + * sqliteResultSet Constructor + * + * @access private + * @param string $record + * @param string $resultId + */ + + function sqlite_driver_ResultSet( $resultId, $connectionId ) + { + $this->fields = array(); + $this->connectionId = $connectionId; + $this->record = array(); + $this->resultId = $resultId; + $this->EOF = false; + } + + /** + * Frees resultset + * + * @access public + */ + + function close() + { + $this->fields = array(); + $this->resultId = false; + } + + /** + * Returns field name from select query + * + * @access public + * @param string $field + * @return string Field name + */ + + function fields( $field ) + { + if(empty($field)) + { + return $this->fields; + } + else + { + return $this->fields[$field]; + } + } + + /** + * Returns numrows from select query + * + * @access public + * @return integer Numrows + */ + + function RecordCount() + { + return $this->_numOfRows; + } + + /** + * Returns num of fields from select query + * + * @access public + * @return integer numfields + */ + + function FieldCount() + { + return $this->_numOfFields; + } + + /** + * Returns next record + * + * @access public + */ + + function MoveNext() + { + if (@$this->fields = sqlite_fetch_array($this->resultId,$this->fetchMode)) { + $this->_currentRow += 1; + return true; + } + if (!$this->EOF) { + $this->_currentRow += 1; + $this->EOF = true; + } + return false; + } + + /** + * Move to the first row in the recordset. Many databases do NOT support this. + * + * @return true or false + */ + + function MoveFirst() + { + if ($this->_currentRow == 0) return true; + return $this->Move(0); + } + + /** + * Returns the Last Record + * + * @access public + */ + + function MoveLast() + { + if ($this->EOF) return false; + return $this->Move($this->_numOfRows - 1); + } + + /** + * Random access to a specific row in the recordset. Some databases do not support + * access to previous rows in the databases (no scrolling backwards). + * + * @param rowNumber is the row to move to (0-based) + * + * @return true if there still rows available, or false if there are no more rows (EOF). + */ + + function Move($rowNumber = 0) + { + if ($rowNumber == $this->_currentRow) return true; + $this->EOF = false; + if ($this->_numOfRows > 0){ + if ($rowNumber >= $this->_numOfRows - 1){ + $rowNumber = $this->_numOfRows - 1; + } + } + + if ($this->_seek($rowNumber)) { + $this->_currentRow = $rowNumber; + if ($this->_fetch()) { + return true; + } + $this->fields = false; + } + $this->EOF = true; + return false; + } + + /** + * Perform Seek to specific row + * + * @access private + */ + + function _seek($row) + { + if ($this->_numOfRows == 0) return false; + return @sqlite_seek($this->resultId,$row); + } + + /** + * Fills field array with first database element when query initially executed + * + * @access private + */ + + function _fetch() + { + $this->fields = @sqlite_fetch_array($this->resultId,$this->fetchMode); + return is_array($this->fields); + } + + /** + * Check to see if last record reached + * + * @access public + */ + + function EOF() + { + if( $this->_currentRow < $this->_numOfRows) + { + return false; + } + else + { + $this->EOF = true; + return true; + } + } + + /** + * Returns All Records in an array + * + * @access public + * @param [nRows] is the number of rows to return. -1 means every row. + */ + + function &GetArray($nRows = -1) + { + $results = array(); + $cnt = 0; + while (!$this->EOF && $nRows != $cnt) { + $results[] = $this->fields; + $this->MoveNext(); + $cnt++; + } + return $results; + } + + function &GetRows($nRows = -1) + { + $arr =& $this->GetArray($nRows); + return $arr; + } + + function &GetAll($nRows = -1) + { + $arr =& $this->GetArray($nRows); + return $arr; + } + + /** + * Fetch field information for a table. + * + * @return object containing the name, type and max_length + */ + function FetchField($fieldOffset = -1) + { + $fieldObject = new ADOFieldObject; + $fieldObject->name = sqlite_field_name($this->resultId, $fieldOffset); + $fieldObject->type = 'VARCHAR'; + $fieldObject->max_length = -1; + return $fieldObject; + } +} +?> \ No newline at end of file diff --git a/lib/adodb/adodbSQL_drivers/sqlite/sqlite_extend_module.inc b/lib/adodb/adodbSQL_drivers/sqlite/sqlite_extend_module.inc new file mode 100644 index 0000000..5312208 --- /dev/null +++ b/lib/adodb/adodbSQL_drivers/sqlite/sqlite_extend_module.inc @@ -0,0 +1,153 @@ +Execute($sql, $inputarr); + if ($result) { + $data =& $result->GetAssoc($force_array, $first2cols); + $result->Close(); + } + return $data; + } + + /** + * Generates a sequence id and stores it in $this->genID; + * GenID is only available if $this->hasGenID = true; + * + * @param seqname name of sequence to use + * @param startID if sequence does not exist, start at this ID + * @return 0 if not supported, otherwise a sequence id + */ + + var $_genSeqSQL = "create table %s (id integer)"; + var $_dropSeqSQL = 'drop table %s'; + var $genID = 0; + + function GenID($seqname='adodbseq', $startID=1) + { + $MAXLOOPS = 100; + while (--$MAXLOOPS>=0) { + @($num = $this->GetOne("select id from $seq")); + if ($num === false) { + $this->Execute(sprintf($this->_genSeqSQL, $seq)); + $start -= 1; + $num = '0'; + $result = $this->Execute("insert into $seq values($start)"); + if (!$result) + return false; + } + $this->Execute("update $seq set id=id+1 where id=$num"); + + if ($this->affected_rows() > 0) { + $num += 1; + $this->genID = $num; + return $num; + } + } + if ($fn = $this->raiseErrorFn) { + $fn($this->databaseType, 'GENID',-32000,"Unable to generate unique id after $MAXLOOPS attempts", $seq, $num); + } + return false; + } + + function CreateSequence($seqname='adodbseq', $start=1) + { + $ok = $this->Execute(sprintf($this->_genSeqSQL, $seqname)); + if (!$ok) + return false; + $start -= 1; + return $this->Execute("insert into $seqname values($start)"); + } + + function DropSequence($seqname) + { + return $this->Execute(sprintf($this->_dropSeqSQL, $seqname)); + } + +} + +eval('class sqlite_extend_resultset_EXTENDER extends '. $last_module . '_ResultSet { }'); + +class sqlite_extend_ResultSet extends sqlite_extend_resultset_EXTENDER +{ + function &GetAssoc($force_array = false, $first2cols = false) + { + $results = false; + + if ($this->_numOfFields > 1) { + $numIndex = isset($this->fields[0]); + $results = array(); + if (!$first2cols && ($this->_numOfFields > 2 || $force_array)) { + if ($numIndex) { + while (!$this->EOF) { + $results[trim($this->fields[0])] = array_slice($this->fields, 1); + $this->MoveNext(); + } + } else { + while (!$this->EOF) { + $results[trim(reset($this->fields))] = array_slice($this->fields, 1); + $this->MoveNext(); + } + } + } else { + if ($numIndex) { + while (!$this->EOF) { + $results[trim(($this->fields[0]))] = $this->fields[1]; + $this->MoveNext(); + } + } else { + while (!$this->EOF) { + $v1 = trim(reset($this->fields)); + $v2 = ''.next($this->fields); + $results[$v1] = $v2; + $this->MoveNext(); + } + } + } + } + return $results; + } + + function PO_RecordCount($table="", $condition="") + { + $lnumrows = $this->_numOfRows; + if($lnumrows == -1 && $this->connectionId) + { + if($table) + { + if ($condition) + $condition = " WHERE " . $condition; + $resultrows = &$this->connectionId->Execute("SELECT COUNT(*) FROM $table $condition"); + if ($resultrows) + $lnumrows = reset($resultrows->fields); + } + } + return $lnumrows; + } + + function CurrentRow() + { + return $this->_currentRow; + } + + function AbsolutePosition() + { + return $this->_currentRow; + } + + function NextRecordSet() + { + return false; + } + +} +?> \ No newline at end of file diff --git a/lib/adodb/adodbSQL_drivers/sqlite/sqlite_meta_module.inc b/lib/adodb/adodbSQL_drivers/sqlite/sqlite_meta_module.inc new file mode 100644 index 0000000..f5c00fc --- /dev/null +++ b/lib/adodb/adodbSQL_drivers/sqlite/sqlite_meta_module.inc @@ -0,0 +1,533 @@ +ErrorNo(); + + return adodb_error($this->dataProvider,$this->databaseType,$err); + } + + function MetaErrorMsg($errno) + { + include_once(ADODB_DIR."/adodb-error.inc.php"); + return adodb_errormsg($errno); + } + + /** + * @returns an array with the primary key columns in it. + */ + function MetaPrimaryKeys($table, $owner=false) + { + // owner not used in base class - see oci8 + $p = array(); + $objs =& $this->MetaColumns($table); + if ($objs) { + foreach($objs as $v) { + if (!empty($v->primary_key)) + $p[] = $v->name; + } + } + if (sizeof($p)) return $p; + if (function_exists('ADODB_VIEW_PRIMARYKEYS')) + return ADODB_VIEW_PRIMARYKEYS($this->databaseType, $this->database, $table, $owner); + return false; + } + + /** + * @returns assoc array where keys are tables, and values are foreign keys + */ + function MetaForeignKeys($table, $owner=false, $upper=false) + { + return false; + } + + // not the fastest implementation - quick and dirty - jlim + // for best performance, use the actual $rs->MetaType(). + function MetaType($t,$len=-1,$fieldobj=false) + { + if (empty($this->_metars)) { + $rsclass = $this->last_module_name . "_ResultSet"; + $this->_metars =& new $rsclass(false,$this->fetchMode); + } + + return $this->_metars->MetaType($t,$len,$fieldobj); + } + + /** + * return the databases that the driver can connect to. + * Some databases will return an empty array. + * + * @return an array of database names. + */ + function MetaDatabases() + { + global $ADODB_FETCH_MODE; + + if ($this->metaDatabasesSQL) { + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + + if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); + + $arr = $this->GetCol($this->metaDatabasesSQL); + if (isset($savem)) $this->SetFetchMode($savem); + $ADODB_FETCH_MODE = $save; + + return $arr; + } + + return false; + } + + /** + * @param ttype can either be 'VIEW' or 'TABLE' or false. + * If false, both views and tables are returned. + * "VIEW" returns only views + * "TABLE" returns only tables + * @param showSchema returns the schema/user with the table name, eg. USER.TABLE + * @param mask is the input mask - only supported by oci8 and postgresql + * + * @return array of tables for current database. + */ + + function &MetaTables($ttype=false,$showSchema=false,$mask=false) + { + global $ADODB_FETCH_MODE; + + $false = false; + if ($mask) { + return $false; + } + if ($this->metaTablesSQL) { + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + + if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); + + $rs = $this->Execute($this->metaTablesSQL); + if (isset($savem)) $this->SetFetchMode($savem); + $ADODB_FETCH_MODE = $save; + + if ($rs === false) return $false; + $arr =& $rs->GetArray(); + $arr2 = array(); + + if ($hast = ($ttype && isset($arr[0][1]))) { + $showt = strncmp($ttype,'T',1); + } + + for ($i=0; $i < sizeof($arr); $i++) { + if ($hast) { + if ($showt == 0) { + if (strncmp($arr[$i][1],'T',1) == 0) $arr2[] = trim($arr[$i][0]); + } else { + if (strncmp($arr[$i][1],'V',1) == 0) $arr2[] = trim($arr[$i][0]); + } + } else + $arr2[] = trim($arr[$i][0]); + } + $rs->Close(); + return $arr2; + } + return $false; + } + + function _findschema(&$table,&$schema) + { + if (!$schema && ($at = strpos($table,'.')) !== false) { + $schema = substr($table,0,$at); + $table = substr($table,$at+1); + } + } + + /** + * List columns in a database as an array of ADOFieldObjects. + * See top of file for definition of object. + * + * @param $table table name to query + * @param $normalize makes table name case-insensitive (required by some databases) + * @schema is optional database schema to use - not supported by all databases. + * + * @return array of ADOFieldObjects for current table. + */ + function &MetaColumns($tab) + { + global $ADODB_FETCH_MODE; + $false = false; + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; + if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); + $rs = $this->Execute("PRAGMA table_info('$tab')"); + if (isset($savem)) $this->SetFetchMode($savem); + if (!$rs) { + $ADODB_FETCH_MODE = $save; + return $false; + } + $arr = array(); + while ($r = $rs->FetchRow()) { + $type = explode('(',$r['type']); + $size = ''; + if (sizeof($type)==2) + $size = trim($type[1],')'); + $fn = strtoupper($r['name']); + $fld = new ADOFieldObject; + $fld->name = $r['name']; + $fld->type = $type[0]; + $fld->max_length = $size; + $fld->not_null = $r['notnull']; + $fld->default_value = $r['dflt_value']; + $fld->scale = 0; + if ($save == ADODB_FETCH_NUM) $arr[] = $fld; + else $arr[strtoupper($fld->name)] = $fld; + } + $rs->Close(); + $ADODB_FETCH_MODE = $save; + return $arr; + } + + /** + * List indexes on a table as an array. + * @param table table name to query + * @param primary true to only show primary keys. Not actually used for most databases + * + * @return array of indexes on current table. Each element represents an index, and is itself an associative array. + + Array ( + [name_of_index] => Array + ( + [unique] => true or false + [columns] => Array + ( + [0] => firstname + [1] => lastname + ) + ) + */ + function &MetaIndexes($table, $primary = FALSE, $owner=false) + { + $false = false; + // save old fetch mode + global $ADODB_FETCH_MODE; + $save = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_NUM; + if ($this->fetchMode !== FALSE) { + $savem = $this->SetFetchMode(FALSE); + } + $SQL=sprintf("SELECT name,sql FROM sqlite_master WHERE type='index' AND tbl_name='%s'", strtolower($table)); + $rs = $this->Execute($SQL); + if (!is_object($rs)) { + if (isset($savem)) + $this->SetFetchMode($savem); + $ADODB_FETCH_MODE = $save; + return $false; + } + + $indexes = array (); + while ($row = $rs->FetchRow()) { + if ($primary && preg_match("/primary/i",$row[1]) == 0) continue; + if (!isset($indexes[$row[0]])) { + + $indexes[$row[0]] = array( + 'unique' => preg_match("/unique/i",$row[1]), + 'columns' => array()); + } + /** + * There must be a more elegant way of doing this, + * the index elements appear in the SQL statement + * in cols[1] between parentheses + * e.g CREATE UNIQUE INDEX ware_0 ON warehouse (org,warehouse) + */ + $cols = explode("(",$row[1]); + $cols = explode(")",$cols[1]); + array_pop($cols); + $indexes[$row[0]]['columns'] = $cols; + } + if (isset($savem)) { + $this->SetFetchMode($savem); + $ADODB_FETCH_MODE = $save; + } + return $indexes; + } + + /** + * List columns names in a table as an array. + * @param table table name to query + * + * @return array of column names for current table. + */ + function &MetaColumnNames($table, $numIndexes=false,$useattnum=false /* only for postgres */) + { + $objarr =& $this->MetaColumns($table); + if (!is_array($objarr)) { + $false = false; + return $false; + } + $arr = array(); + if ($numIndexes) { + $i = 0; + if ($useattnum) { + foreach($objarr as $v) + $arr[$v->attnum] = $v->name; + + } else + foreach($objarr as $v) $arr[$i++] = $v->name; + } else + foreach($objarr as $v) $arr[strtoupper($v->name)] = $v->name; + + return $arr; + } + + function MetaTransaction($mode,$db) + { + $mode = strtoupper($mode); + $mode = str_replace('ISOLATION LEVEL ','',$mode); + + switch($mode) { + + case 'READ UNCOMMITTED': + switch($db) { + case 'oci8': + case 'oracle': + return 'ISOLATION LEVEL READ COMMITTED'; + default: + return 'ISOLATION LEVEL READ UNCOMMITTED'; + } + break; + + case 'READ COMMITTED': + return 'ISOLATION LEVEL READ COMMITTED'; + break; + + case 'REPEATABLE READ': + switch($db) { + case 'oci8': + case 'oracle': + return 'ISOLATION LEVEL SERIALIZABLE'; + default: + return 'ISOLATION LEVEL REPEATABLE READ'; + } + break; + + case 'SERIALIZABLE': + return 'ISOLATION LEVEL SERIALIZABLE'; + break; + + default: + return $mode; + } + } + +} + +eval('class sqlite_meta_resultset_EXTENDER extends '. $last_module . '_ResultSet { }'); + +class sqlite_meta_ResultSet extends sqlite_meta_resultset_EXTENDER +{ + /** + * Get the metatype of the column. This is used for formatting. This is because + * many databases use different names for the same type, so we transform the original + * type to our standardised version which uses 1 character codes: + * + * @param t is the type passed in. Normally is ADOFieldObject->type. + * @param len is the maximum length of that field. This is because we treat character + * fields bigger than a certain size as a 'B' (blob). + * @param fieldobj is the field object returned by the database driver. Can hold + * additional info (eg. primary_key for mysql). + * + * @return the general type of the data: + * C for character < 250 chars + * X for teXt (>= 250 chars) + * B for Binary + * N for numeric or floating point + * D for date + * T for timestamp + * L for logical/Boolean + * I for integer + * R for autoincrement counter/integer + * + * + */ + function MetaType($t,$len=-1,$fieldobj=false) + { + if (is_object($t)) { + $fieldobj = $t; + $t = $fieldobj->type; + $len = $fieldobj->max_length; + } + + $len = -1; // mysql max_length is not accurate + switch (strtoupper($t)) { + case 'STRING': + case 'CHAR': + case 'VARCHAR': + case 'TINYBLOB': + case 'TINYTEXT': + case 'ENUM': + case 'SET': + if ($len <= $this->blobSize) return 'C'; + + case 'TEXT': + case 'LONGTEXT': + case 'MEDIUMTEXT': + return 'X'; + + // php_mysql extension always returns 'blob' even if 'text' + // so we have to check whether binary... + case 'IMAGE': + case 'LONGBLOB': + case 'BLOB': + case 'MEDIUMBLOB': + return !empty($fieldobj->binary) ? 'B' : 'X'; + + case 'YEAR': + case 'DATE': return 'D'; + + case 'TIME': + case 'DATETIME': + case 'TIMESTAMP': return 'T'; + + case 'INT': + case 'INTEGER': + case 'BIGINT': + case 'TINYINT': + case 'MEDIUMINT': + case 'SMALLINT': + + if (!empty($fieldobj->primary_key)) return 'R'; + else return 'I'; + + default: + static $typeMap = array( + 'VARCHAR' => 'C', + 'VARCHAR2' => 'C', + 'CHAR' => 'C', + 'C' => 'C', + 'STRING' => 'C', + 'NCHAR' => 'C', + 'NVARCHAR' => 'C', + 'VARYING' => 'C', + 'BPCHAR' => 'C', + 'CHARACTER' => 'C', + 'INTERVAL' => 'C', # Postgres + 'MACADDR' => 'C', # postgres + ## + 'LONGCHAR' => 'X', + 'TEXT' => 'X', + 'NTEXT' => 'X', + 'M' => 'X', + 'X' => 'X', + 'CLOB' => 'X', + 'NCLOB' => 'X', + 'LVARCHAR' => 'X', + ## + 'BLOB' => 'B', + 'IMAGE' => 'B', + 'BINARY' => 'B', + 'VARBINARY' => 'B', + 'LONGBINARY' => 'B', + 'B' => 'B', + ## + 'YEAR' => 'D', // mysql + 'DATE' => 'D', + 'D' => 'D', + ## + 'UNIQUEIDENTIFIER' => 'C', # MS SQL Server + ## + 'TIME' => 'T', + 'TIMESTAMP' => 'T', + 'DATETIME' => 'T', + 'TIMESTAMPTZ' => 'T', + 'T' => 'T', + 'TIMESTAMP WITHOUT TIME ZONE' => 'T', // postgresql + ## + 'BOOL' => 'L', + 'BOOLEAN' => 'L', + 'BIT' => 'L', + 'L' => 'L', + ## + 'COUNTER' => 'R', + 'R' => 'R', + 'SERIAL' => 'R', // ifx + 'INT IDENTITY' => 'R', + ## + 'INT' => 'I', + 'INT2' => 'I', + 'INT4' => 'I', + 'INT8' => 'I', + 'INTEGER' => 'I', + 'INTEGER UNSIGNED' => 'I', + 'SHORT' => 'I', + 'TINYINT' => 'I', + 'SMALLINT' => 'I', + 'I' => 'I', + ## + 'LONG' => 'N', // interbase is numeric, oci8 is blob + 'BIGINT' => 'N', // this is bigger than PHP 32-bit integers + 'DECIMAL' => 'N', + 'DEC' => 'N', + 'REAL' => 'N', + 'DOUBLE' => 'N', + 'DOUBLE PRECISION' => 'N', + 'SMALLFLOAT' => 'N', + 'FLOAT' => 'N', + 'NUMBER' => 'N', + 'NUM' => 'N', + 'NUMERIC' => 'N', + 'MONEY' => 'N', + + ## informix 9.2 + 'SQLINT' => 'I', + 'SQLSERIAL' => 'I', + 'SQLSMINT' => 'I', + 'SQLSMFLOAT' => 'N', + 'SQLFLOAT' => 'N', + 'SQLMONEY' => 'N', + 'SQLDECIMAL' => 'N', + 'SQLDATE' => 'D', + 'SQLVCHAR' => 'C', + 'SQLCHAR' => 'C', + 'SQLDTIME' => 'T', + 'SQLINTERVAL' => 'N', + 'SQLBYTES' => 'B', + 'SQLTEXT' => 'X', + ## informix 10 + "SQLINT8" => 'I8', + "SQLSERIAL8" => 'I8', + "SQLNCHAR" => 'C', + "SQLNVCHAR" => 'C', + "SQLLVARCHAR" => 'X', + "SQLBOOL" => 'L' + ); + + $tmap = false; + $t = strtoupper($t); + $tmap = (isset($typeMap[$t])) ? $typeMap[$t] : 'N'; + if ($t == 'LONG' && $this->dataProvider == 'oci8') return 'B'; + return $tmap; + } + } + +} + +?> \ No newline at end of file diff --git a/lib/adodb/adodbSQL_drivers/sqlite/sqlite_transaction_module.inc b/lib/adodb/adodbSQL_drivers/sqlite/sqlite_transaction_module.inc new file mode 100644 index 0000000..bc1e055 --- /dev/null +++ b/lib/adodb/adodbSQL_drivers/sqlite/sqlite_transaction_module.inc @@ -0,0 +1,132 @@ +transOff > 0) { + $this->transOff += 1; + return; + } + $this->transaction_status = true; + + if ($this->debug && $this->transCnt > 0) + ADOConnection::outp("Bad Transaction: StartTrans called within BeginTrans"); + + $this->BeginTrans(); + $this->transOff = 1; + } + + function BeginTrans() + { + if ($this->transOff) + return true; + + $ret = $this->Execute("BEGIN TRANSACTION"); + $this->transCnt += 1; + return true; + } + + function CompleteTrans($autoComplete = true) + { + if ($this->transOff > 1) { + $this->transOff -= 1; + return true; + } + $this->transOff = 0; + if ($this->transaction_status && $autoComplete) { + if (!$this->CommitTrans()) { + $this->transaction_status = false; + if ($this->debug) + ADOConnection::outp("Smart Commit failed"); + } else + if ($this->debug) + ADOConnection::outp("Smart Commit occurred"); + } else { + $this->RollbackTrans(); + if ($this->debug) + ADOCOnnection::outp("Smart Rollback occurred"); + } + return $this->transaction_status; + } + + function CommitTrans($ok=true) + { + if ($this->transOff) + return true; + + if (!$ok) + return $this->RollbackTrans(); + + $ret = $this->Execute("COMMIT"); + if ($this->transCnt>0) + $this->transCnt -= 1; + + return !empty($ret); + } + + function RollbackTrans() + { + if ($this->transOff) + return true; + + $ret = $this->Execute("ROLLBACK"); + if ($this->transCnt>0) + $this->transCnt -= 1; + + return !empty($ret); + } + + function FailTrans() + { + if ($this->debug) + if ($this->transOff == 0) { + ADOConnection::outp("FailTrans outside StartTrans/CompleteTrans"); + } else { + ADOConnection::outp("FailTrans was called"); + } + $this->transaction_status = false; + } + + function HasFailedTrans() + { + if ($this->transOff > 0) + return $this->transaction_status == false; + + return false; + } + + function RowLock($tables,$where,$flds='1 as ignore') + { + return false; + } + + function CommitLock($table) + { + return $this->CommitTrans(); + } + + function RollbackLock($table) + { + return $this->RollbackTrans(); + } + +} + +eval('class sqlite_transaction_resultset_EXTENDER extends '. $last_module . '_ResultSet { }'); + +class sqlite_transaction_ResultSet extends sqlite_transaction_resultset_EXTENDER +{ +} +?> \ No newline at end of file diff --git a/lib/adodb/generic_modules/adodblite_module.inc b/lib/adodb/generic_modules/adodblite_module.inc new file mode 100644 index 0000000..f377c67 --- /dev/null +++ b/lib/adodb/generic_modules/adodblite_module.inc @@ -0,0 +1,41 @@ +Execute($sql, $inputarr); + if ($result) { + $data =& $result->GetToFieldArray($fieldname); + $result->Close(); + } + return $data; + } + +} + +eval('class adodblite_resultset_EXTENDER extends ' . $last_module . '_ResultSet { }'); + +class adodblite_ResultSet extends adodblite_resultset_EXTENDER +{ + function &GetToFieldArray($fieldname = false) + { + $results = false; + if(!$fieldname) + { + return $results; + } + + while (!$this->EOF) { + $results[$this->fields[$fieldname]] = $this->fields; + $this->MoveNext(); + } + return $results; + } + +} +?> \ No newline at end of file diff --git a/lib/adodb/generic_modules/menu_module.inc b/lib/adodb/generic_modules/menu_module.inc new file mode 100644 index 0000000..6f6fb21 --- /dev/null +++ b/lib/adodb/generic_modules/menu_module.inc @@ -0,0 +1,226 @@ +'; + if($blank1stItem) + if(is_string($blank1stItem)) + { + $barr = explode(':',$blank1stItem); + if(sizeof($barr) == 1) + $barr[] = ''; + $s .= "\n"; + } else $s .= "\n"; + + if($this->FieldCount() > 1) + $hasvalue=true; + else $compareFields0 = true; + + $value = ''; + $optgroup = null; + $firstgroup = true; + $fieldsize = $this->FieldCount(); + while(!$this->EOF) + { + $zval = rtrim(reset($this->fields)); + + if($blank1stItem && $zval=="") + { + $this->MoveNext(); + continue; + } + + if($fieldsize > 1) + { + if(isset($this->fields[1])) + $zval2 = rtrim($this->fields[1]); + else + $zval2 = rtrim(next($this->fields)); + } + $selected = ($compareFields0) ? $zval : $zval2; + + $group = ''; + if($fieldsize > 2) + { + $group = rtrim($this->fields[2]); + } + + if($optgroup != $group) + { + $optgroup = $group; + if($firstgroup) + { + $firstgroup = false; + $s .="\n"; + $s .="\n"; + } + return $s ."\n\n"; + } + + function GetMenu2($name,$defstr='',$blank1stItem=true,$multiple=false,$size=0, $selectAttr='') + { + return $this->GetMenu($name, $defstr, $blank1stItem, $multiple, $size, $selectAttr, false); + } + + function GetMenu3($name, $defstr='', $blank1stItem=true, $multiple=false, $size=0, $selectAttr='') + { + $hasvalue = false; + + if($multiple or is_array($defstr)) + { + if($size==0) + $size=5; + $attr = ' multiple size="'.$size.'"'; + if(!strpos($name,'[]')) + $name .= '[]'; + } + else if($size) + $attr = ' size="'.$size.'"'; + else $attr =''; + + $s = '\n"; + } + +} + +?> \ No newline at end of file diff --git a/lib/adodb/generic_modules/object_module.inc b/lib/adodb/generic_modules/object_module.inc new file mode 100644 index 0000000..d8e7ae0 --- /dev/null +++ b/lib/adodb/generic_modules/object_module.inc @@ -0,0 +1,99 @@ +FetchObject(false); + return $object; + } + + function &FetchObject($isupper=true) + { + if (empty($this->_obj)) + { + $this->_obj = new ADOFetchObj(); + $this->_names = array(); + for ($i=0; $i <$this->_numOfFields; $i++) + { + $field = $this->FetchField($i); + $this->_names[] = $field->name; + } + } + $i = 0; + if (PHP_VERSION >= 5) + $object = clone($this->_obj); + else $object = $this->_obj; + + for ($i=0; $i <$this->_numOfFields; $i++) + { + $name = $this->_names[$i]; + if ($isupper) + $newname = strtoupper($name); + else $newname = $name; + + $object->$newname = $this->Fields($name); + } + return $object; + } + + function &FetchNextObj() + { + $object =& $this->FetchNextObject(false); + return $object; + } + + function &FetchNextObject($isupper=true) + { + $object = false; + if ($this->_numOfRows != 0 && !$this->EOF) { + $object = $this->FetchObject($isupper); + $this->_currentRow++; + if ($this->_fetch()) + return $object; + } + $this->EOF = true; + return $object; + } + + /* Load a list of database objects + * @param string The field name of a primary key + * @return array If key is empty as sequential list of returned records. + * If key is not empty then the returned array is indexed by the value + * the database key. Returns null if the query fails. + */ + function &loadObjectList( $key='' ) + { + $array = array(); + while ($row = $this->FetchNextObject(false)) + { + if ($key) + { + $array[$row->$key] = $row; + } + else + { + $array[] = $row; + } + } + return $array; + } + +} +?> \ No newline at end of file diff --git a/lib/adodb/generic_modules/pear_module.inc b/lib/adodb/generic_modules/pear_module.inc new file mode 100644 index 0000000..01a3465 --- /dev/null +++ b/lib/adodb/generic_modules/pear_module.inc @@ -0,0 +1,192 @@ +SetFetchMode($mode) + * + * @param mode The fetchmode ADODB_FETCH_ASSOC or ADODB_FETCH_NUM + * @returns The previous fetch mode + */ + function SetFetchMode($mode) + { + GLOBAL $ADODB_FETCH_MODE; + $old = $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = $mode; + return $old; + } + + /** + * Returns the last record id of an inserted item + * Usage: $db->GetCol($sql); + * + * @access public + */ + + function GetCol($sql, $inputarr = false, $trim = false) + { + $data = false; + $result =& $this->do_query($sql, -1, -1, $inputarr); + if ($result) { + $data = array(); + while (!$result->EOF) { + $data[] = ($trim) ? trim(reset($result->fields)) : reset($result->fields); + $result->MoveNext(); + } + $result->Close(); + } + return $data; + } + + /** + * Return first element of first row of sql statement. Recordset is disposed + * for you. + * + * Usage: $db->GetOne($sql); + * @access public + */ + + function &GetOne($sql, $inputarr = false) + { + $data =& $this->GetRow($sql, $inputarr, true); + return $data; + } + + /** + * Return one row of sql statement. Recordset is disposed for you. + * + * Usage: $db->GetRow($sql); + * @access public + */ + + function &GetRow($sql, $inputarr = false, $getone = false) + { + $data = false; + $result =& $this->do_query($sql, -1, -1, $inputarr); + if ($result) { + if ($getone) + { + if (!$result->EOF) $data = reset($result->fields); + } + else + { + if (!$result->EOF) $data = $result->fields; + else $data = array(); + } + $result->Close(); + } + return $data; + } + + /** + * PEAR DB Compat - do not use internally + */ + function &Query($sql, $inputarr = false) + { + $rs =& $this->do_query($sql, -1, -1, $inputarr); + return $rs; + } + + /** + * PEAR DB Compat - do not use internally + */ + function &LimitQuery($sql, $offset, $nrows, $inputarr = false) + { + $rs =& $this->do_query($sql, $nrows, $offset, $inputarr); + return $rs; + } + + /** + * PEAR DB Compat - do not use internally + */ + function Disconnect() + { + return $this->Close(); + } + + /** + * PEAR DB Compat - do not use internally + */ + function ErrorNative() + { + return $this->ErrorNo(); + } + + /** + * PEAR DB Compat - do not use internally + */ + function Quote($string) + { + return $this->qstr($string, false); + } + +} + +eval('class pear_resultset_EXTENDER extends ' . $last_module . '_ResultSet { }'); + +class pear_ResultSet extends pear_resultset_EXTENDER +{ + /** + * PEAR DB Compatable Command + */ + function Free() + { + return $this->Close(); + } + + /** + * PEAR DB Compatable Command + */ + function NumRows() + { + return $this->_numOfRows; + } + + /** + * PEAR DB Compatable Command + */ + function NumCols() + { + return $this->_numOfFields; + } + + /** + * Fetch a row, returning false if no more rows. + * PEAR DB Compatable Command + * + * @return false or array containing the current record + */ + function FetchRow() + { + if ($this->EOF) { + $false = false; + return $false; + } + $arr = $this->fields; + $this->_currentRow++; + if (!$this->_fetch()) $this->EOF = true; + return $arr; + } + + /** + * Fetch a row, returning PEAR_Error if no more rows. + * PEAR DB Compatable Command + * + */ + function FetchInto(&$arr) + { + $false = false; + $true = 1; + if ($this->EOF) return $false; + $arr = $this->fields; + $this->MoveNext(); + return $true; + } + +} +?> \ No newline at end of file diff --git a/lib/adodb/license.txt b/lib/adodb/license.txt new file mode 100644 index 0000000..55ed0b1 --- /dev/null +++ b/lib/adodb/license.txt @@ -0,0 +1,144 @@ +GNU LESSER GENERAL PUBLIC LICENSE +Version 2.1, February 1999 + +Copyright (C) 1991, 1999 Free Software Foundation, Inc. +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + +Preamble +The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. + +This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. + +When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. + +To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. + +For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. + +We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. + +To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. + +Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. + +Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. + +When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. + +We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. + +For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. + +In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. + +Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. + +The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. + + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". + +A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. + +The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) + +"Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. + +Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. + +1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. + +You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: + + +a) The modified work must itself be a software library. +b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. +c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. +d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. +(For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. + +3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. + +Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. + +This option is useful when you wish to copy part of the code of the Library into a program that is not a library. + +4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. + +If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. + +5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. + +However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. + +When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. + +If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) + +Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. + +6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. + +You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: + + +a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) +b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. +c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. +d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. +e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. +For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. + +It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. + +7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: + + +a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. +b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. +8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. + +9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. + +10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. + +11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. + +12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. + +13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. + +14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. + +NO WARRANTY + +15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + + +END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/lib/dwoo.php b/lib/dwoo.php new file mode 100644 index 0000000..4ce4010 --- /dev/null +++ b/lib/dwoo.php @@ -0,0 +1,27 @@ + \ No newline at end of file diff --git a/lib/dwoo/Dwoo.compiled.php b/lib/dwoo/Dwoo.compiled.php new file mode 100644 index 0000000..34e682a --- /dev/null +++ b/lib/dwoo/Dwoo.compiled.php @@ -0,0 +1 @@ + array ( 'class' => 'Dwoo_Template_File', 'compiler' => null ), 'string' => array ( 'class' => 'Dwoo_Template_String', 'compiler' => null ) ); protected $loader = null; protected $template = null; protected $runtimePlugins; protected $data; protected $scope; protected $scopeTree; protected $stack; protected $curBlock; protected $buffer; protected $pluginProxy; public function __construct($compileDir = KU_CACHEDTEMPLATEDIR, $cacheDir = KU_CACHEDTEMPLATEDIR) { if ($compileDir !== null) { $this->setCompileDir($compileDir); } if ($cacheDir !== null) { $this->setCacheDir($cacheDir); } } public function __clone() { $this->template = null; unset($this->data); } public function output($tpl, $data = array(), Dwoo_ICompiler $compiler = null) { return $this->get($tpl, $data, $compiler, true); } public function get($_tpl, $data = array(), $_compiler = null, $_output = false) { if ($this->template instanceof Dwoo_ITemplate) { $proxy = clone $this; return $proxy->get($_tpl, $data, $_compiler, $_output); } if ($_tpl instanceof Dwoo_ITemplate) { } elseif (is_string($_tpl) && file_exists($_tpl)) { $_tpl = new Dwoo_Template_File($_tpl); } else { throw new Dwoo_Exception('Dwoo->get/Dwoo->output\'s first argument must be a Dwoo_ITemplate (i.e. Dwoo_Template_File) or a valid path to a template file', E_USER_NOTICE); } $this->template = $_tpl; if ($data instanceof Dwoo_IDataProvider) { $this->data = $data->getData(); } elseif (is_array($data)) { $this->data = $data; } else { throw new Dwoo_Exception('Dwoo->get/Dwoo->output\'s data argument must be a Dwoo_IDataProvider object (i.e. Dwoo_Data) or an associative array', E_USER_NOTICE); } $this->initGlobals($_tpl); $this->initRuntimeVars($_tpl); $file = $_tpl->getCachedTemplate($this); $doCache = $file === true; $cacheLoaded = is_string($file); if ($cacheLoaded === true) { if ($_output === true) { include $file; $this->template = null; } else { ob_start(); include $file; $this->template = null; return ob_get_clean(); } } else { if ($doCache === true) { $dynamicId = uniqid(); } $out = include $_tpl->getCompiledTemplate($this, $_compiler); if ($out === false) { $_tpl->forceCompilation(); $out = include $_tpl->getCompiledTemplate($this, $_compiler); } if ($doCache === true) { $out = preg_replace('/(<%|%>|<\?php|<\?|\?>)/', '', $out); if (!class_exists('Dwoo_plugin_dynamic', false)) { $this->getLoader()->loadPlugin('dynamic'); } $out = Dwoo_Plugin_dynamic::unescape($out, $dynamicId); } foreach ($this->filters as $filter) { if (is_array($filter) && $filter[0] instanceof Dwoo_Filter) { $out = call_user_func($filter, $out); } else { $out = call_user_func($filter, $this, $out); } } if ($doCache === true) { $file = $_tpl->cache($this, $out); if ($_output === true) { include $file; $this->template = null; } else { ob_start(); include $file; $this->template = null; return ob_get_clean(); } } else { $this->template = null; if ($_output === true) { echo $out; } else { return $out; } } } } protected function initGlobals(Dwoo_ITemplate $tpl) { $this->globals = array ( 'version' => self::VERSION, 'ad' => 'Powered by Dwoo', 'now' => $_SERVER['REQUEST_TIME'], 'template' => $tpl->getName(), 'charset' => $this->charset, ); } protected function initRuntimeVars(Dwoo_ITemplate $tpl) { $this->runtimePlugins = array(); $this->scope =& $this->data; $this->scopeTree = array(); $this->stack = array(); $this->curBlock = null; $this->buffer = ''; } public function addPlugin($name, $callback, $compilable = false) { $compilable = $compilable ? self::COMPILABLE_PLUGIN : 0; if (is_array($callback)) { if (is_subclass_of(is_object($callback[0]) ? get_class($callback[0]) : $callback[0], 'Dwoo_Block_Plugin')) { $this->plugins[$name] = array('type'=>self::BLOCK_PLUGIN | $compilable, 'callback'=>$callback, 'class'=>(is_object($callback[0]) ? get_class($callback[0]) : $callback[0])); } else { $this->plugins[$name] = array('type'=>self::CLASS_PLUGIN | $compilable, 'callback'=>$callback, 'class'=>(is_object($callback[0]) ? get_class($callback[0]) : $callback[0]), 'function'=>$callback[1]); } } elseif (class_exists($callback, false)) { if (is_subclass_of($callback, 'Dwoo_Block_Plugin')) { $this->plugins[$name] = array('type'=>self::BLOCK_PLUGIN | $compilable, 'callback'=>$callback, 'class'=>$callback); } else { $this->plugins[$name] = array('type'=>self::CLASS_PLUGIN | $compilable, 'callback'=>$callback, 'class'=>$callback, 'function'=>'process'); } } elseif (function_exists($callback)) { $this->plugins[$name] = array('type'=>self::FUNC_PLUGIN | $compilable, 'callback'=>$callback); } else { throw new Dwoo_Exception('Callback could not be processed correctly, please check that the function/class you used exists'); } } public function removePlugin($name) { if (isset($this->plugins[$name])) { unset($this->plugins[$name]); } } public function addFilter($callback, $autoload = false) { if ($autoload) { $class = 'Dwoo_Filter_'.$callback; if (!class_exists($class, false) && !function_exists($class)) { try { $this->getLoader()->loadPlugin($callback); } catch (Dwoo_Exception $e) { if (strstr($callback, 'Dwoo_Filter_')) { throw new Dwoo_Exception('Wrong filter name : '.$callback.', the "Dwoo_Filter_" prefix should not be used, please only use "'.str_replace('Dwoo_Filter_', '', $callback).'"'); } else { throw new Dwoo_Exception('Wrong filter name : '.$callback.', when using autoload the filter must be in one of your plugin dir as "name.php" containg a class or function named "Dwoo_Filter_name"'); } } } if (class_exists($class, false)) { $callback = array(new $class($this), 'process'); } elseif (function_exists($class)) { $callback = $class; } else { throw new Dwoo_Exception('Wrong filter name : '.$callback.', when using autoload the filter must be in one of your plugin dir as "name.php" containg a class or function named "Dwoo_Filter_name"'); } $this->filters[] = $callback; } else { $this->filters[] = $callback; } } public function removeFilter($callback) { if (($index = array_search('Dwoo_Filter_'.$callback, $this->filters, true)) !== false) { unset($this->filters[$index]); } elseif (($index = array_search($callback, $this->filters, true)) !== false) { unset($this->filters[$index]); } else { $class = 'Dwoo_Filter_' . $callback; foreach ($this->filters as $index=>$filter) { if (is_array($filter) && $filter[0] instanceof $class) { unset($this->filters[$index]); break; } } } } public function addResource($name, $class, $compilerFactory = null) { if (strlen($name) < 2) { throw new Dwoo_Exception('Resource names must be at least two-character long to avoid conflicts with Windows paths'); } if (!class_exists($class)) { throw new Dwoo_Exception('Resource class does not exist'); } $interfaces = class_implements($class); if (in_array('Dwoo_ITemplate', $interfaces) === false) { throw new Dwoo_Exception('Resource class must implement Dwoo_ITemplate'); } $this->resources[$name] = array('class'=>$class, 'compiler'=>$compilerFactory); } public function removeResource($name) { unset($this->resources[$name]); if ($name==='file') { $this->resources['file'] = array('class'=>'Dwoo_Template_File', 'compiler'=>null); } } public function setLoader(Dwoo_ILoader $loader) { $this->loader = $loader; } public function getLoader() { if ($this->loader === null) { $this->loader = new Dwoo_Loader($this->getCompileDir()); } return $this->loader; } public function getCustomPlugins() { return $this->plugins; } public function getCacheDir() { if ($this->cacheDir === null) { $this->setCacheDir(dirname(__FILE__).DIRECTORY_SEPARATOR.'cache'.DIRECTORY_SEPARATOR); } return $this->cacheDir; } public function setCacheDir($dir) { $this->cacheDir = rtrim($dir, '/\\').DIRECTORY_SEPARATOR; if (is_writable($this->cacheDir) === false) { throw new Dwoo_Exception('The cache directory must be writable, chmod "'.$this->cacheDir.'" to make it writable'); } } public function getCompileDir() { if ($this->compileDir === null) { $this->setCompileDir(dirname(__FILE__).DIRECTORY_SEPARATOR.'compiled'.DIRECTORY_SEPARATOR); } return $this->compileDir; } public function setCompileDir($dir) { $this->compileDir = rtrim($dir, '/\\').DIRECTORY_SEPARATOR; if (is_writable($this->compileDir) === false) { throw new Dwoo_Exception('The compile directory must be writable, chmod "'.$this->compileDir.'" to make it writable'); } } public function getCacheTime() { return $this->cacheTime; } public function setCacheTime($seconds) { $this->cacheTime = (int) $seconds; } public function getCharset() { return $this->charset; } public function setCharset($charset) { $this->charset = strtolower((string) $charset); } public function getTemplate() { return $this->template; } public function setDefaultCompilerFactory($resourceName, $compilerFactory) { $this->resources[$resourceName]['compiler'] = $compilerFactory; } public function getDefaultCompilerFactory($resourceName) { return $this->resources[$resourceName]['compiler']; } public function setSecurityPolicy(Dwoo_Security_Policy $policy = null) { $this->securityPolicy = $policy; } public function getSecurityPolicy() { return $this->securityPolicy; } public function setPluginProxy(Dwoo_IPluginProxy $pluginProxy) { $this->pluginProxy = $pluginProxy; } public function getPluginProxy() { return $this->pluginProxy; } public function isCached(Dwoo_ITemplate $tpl) { return is_string($tpl->getCachedTemplate($this)); } public function clearCache($olderThan=-1) { $cacheDirs = new RecursiveDirectoryIterator($this->getCacheDir()); $cache = new RecursiveIteratorIterator($cacheDirs); $expired = time() - $olderThan; $count = 0; foreach ($cache as $file) { if ($cache->isDot() || $cache->isDir() || substr($file, -5) !== '.html') { continue; } if ($cache->getCTime() < $expired) { $count += unlink((string) $file) ? 1 : 0; } } return $count; } public function templateFactory($resourceName, $resourceId, $cacheTime = null, $cacheId = null, $compileId = null, Dwoo_ITemplate $parentTemplate = null) { if (isset($this->resources[$resourceName])) { return call_user_func(array($this->resources[$resourceName]['class'], 'templateFactory'), $this, $resourceId, $cacheTime, $cacheId, $compileId, $parentTemplate); } else { throw new Dwoo_Exception('Unknown resource type : '.$resourceName); } } public function isArray($value, $checkIsEmpty=false, $allowNonCountable=false) { if (is_array($value) === true) { if ($checkIsEmpty === false) { return true; } else { return count($value) > 0; } } elseif ($value instanceof Iterator || $value instanceof ArrayAccess) { if ($checkIsEmpty === false) { return true; } else { if ($allowNonCountable === false) { return count($value) > 0; } else { if ($value instanceof Countable) { return count($value) > 0; } else { $value->rewind(); return $value->valid(); } } } } return false; } public function triggerError($message, $level=E_USER_NOTICE) { if (!($tplIdentifier = $this->template->getResourceIdentifier())) { $tplIdentifier = $this->template->getResourceName(); } trigger_error('Dwoo error (in '.$tplIdentifier.') : '.$message, $level); } public function addStack($blockName, array $args=array()) { if (isset($this->plugins[$blockName])) { $class = $this->plugins[$blockName]['class']; } else { $class = 'Dwoo_Plugin_'.$blockName; } if ($this->curBlock !== null) { $this->curBlock->buffer(ob_get_contents()); ob_clean(); } else { $this->buffer .= ob_get_contents(); ob_clean(); } $block = new $class($this); $cnt = count($args); if ($cnt===0) { $block->init(); } elseif ($cnt===1) { $block->init($args[0]); } elseif ($cnt===2) { $block->init($args[0], $args[1]); } elseif ($cnt===3) { $block->init($args[0], $args[1], $args[2]); } elseif ($cnt===4) { $block->init($args[0], $args[1], $args[2], $args[3]); } else { call_user_func_array(array($block,'init'), $args); } $this->stack[] = $this->curBlock = $block; return $block; } public function delStack() { $args = func_get_args(); $this->curBlock->buffer(ob_get_contents()); ob_clean(); $cnt = count($args); if ($cnt===0) { $this->curBlock->end(); } elseif ($cnt===1) { $this->curBlock->end($args[0]); } elseif ($cnt===2) { $this->curBlock->end($args[0], $args[1]); } elseif ($cnt===3) { $this->curBlock->end($args[0], $args[1], $args[2]); } elseif ($cnt===4) { $this->curBlock->end($args[0], $args[1], $args[2], $args[3]); } else { call_user_func_array(array($this->curBlock, 'end'), $args); } $tmp = array_pop($this->stack); if (count($this->stack) > 0) { $this->curBlock = end($this->stack); $this->curBlock->buffer($tmp->process()); } else { $this->curBlock = null; echo $tmp->process(); } unset($tmp); } public function getParentBlock(Dwoo_Block_Plugin $block) { $index = array_search($block, $this->stack, true); if ($index !== false && $index > 0) { return $this->stack[$index-1]; } return false; } public function findBlock($type) { if (isset($this->plugins[$type])) { $type = $this->plugins[$type]['class']; } else { $type = 'Dwoo_Plugin_'.str_replace('Dwoo_Plugin_', '', $type); } $keys = array_keys($this->stack); while (($key = array_pop($keys)) !== false) { if ($this->stack[$key] instanceof $type) { return $this->stack[$key]; } } return false; } protected function getObjectPlugin($class) { if (isset($this->runtimePlugins[$class])) { return $this->runtimePlugins[$class]; } return $this->runtimePlugins[$class] = new $class($this); } public function classCall($plugName, array $params = array()) { $class = 'Dwoo_Plugin_'.$plugName; $plugin = $this->getObjectPlugin($class); $cnt = count($params); if ($cnt===0) { return $plugin->process(); } elseif ($cnt===1) { return $plugin->process($params[0]); } elseif ($cnt===2) { return $plugin->process($params[0], $params[1]); } elseif ($cnt===3) { return $plugin->process($params[0], $params[1], $params[2]); } elseif ($cnt===4) { return $plugin->process($params[0], $params[1], $params[2], $params[3]); } else { return call_user_func_array(array($plugin, 'process'), $params); } } public function arrayMap($callback, array $params) { if ($params[0] === $this) { $addThis = true; array_shift($params); } if ((is_array($params[0]) || ($params[0] instanceof Iterator && $params[0] instanceof ArrayAccess))) { if (empty($params[0])) { return $params[0]; } $out = array(); $cnt = count($params); if (isset($addThis)) { array_unshift($params, $this); $items = $params[1]; $keys = array_keys($items); if (is_string($callback) === false) { while (($i = array_shift($keys)) !== null) { $out[] = call_user_func_array($callback, array(1=>$items[$i]) + $params); } } elseif ($cnt===1) { while (($i = array_shift($keys)) !== null) { $out[] = $callback($this, $items[$i]); } } elseif ($cnt===2) { while (($i = array_shift($keys)) !== null) { $out[] = $callback($this, $items[$i], $params[2]); } } elseif ($cnt===3) { while (($i = array_shift($keys)) !== null) { $out[] = $callback($this, $items[$i], $params[2], $params[3]); } } else { while (($i = array_shift($keys)) !== null) { $out[] = call_user_func_array($callback, array(1=>$items[$i]) + $params); } } } else { $items = $params[0]; $keys = array_keys($items); if (is_string($callback) === false) { while (($i = array_shift($keys)) !== null) { $out[] = call_user_func_array($callback, array($items[$i]) + $params); } } elseif ($cnt===1) { while (($i = array_shift($keys)) !== null) { $out[] = $callback($items[$i]); } } elseif ($cnt===2) { while (($i = array_shift($keys)) !== null) { $out[] = $callback($items[$i], $params[1]); } } elseif ($cnt===3) { while (($i = array_shift($keys)) !== null) { $out[] = $callback($items[$i], $params[1], $params[2]); } } elseif ($cnt===4) { while (($i = array_shift($keys)) !== null) { $out[] = $callback($items[$i], $params[1], $params[2], $params[3]); } } else { while (($i = array_shift($keys)) !== null) { $out[] = call_user_func_array($callback, array($items[$i]) + $params); } } } return $out; } else { return $params[0]; } } public function readVarInto($varstr, $data) { if ($data === null) { return null; } if (is_array($varstr) === false) { preg_match_all('#(\[|->|\.)?([^.[\]-]+)\]?#i', $varstr, $m); } else { $m = $varstr; } unset($varstr); while (list($k, $sep) = each($m[1])) { if ($sep === '.' || $sep === '[' || $sep === '') { if ((is_array($data) || $data instanceof ArrayAccess) && isset($data[$m[2][$k]])) { $data = $data[$m[2][$k]]; } else { return null; } } else { if (is_object($data)) { $data = $data->$m[2][$k]; } else { return null; } } } return $data; } public function readParentVar($parentLevels, $varstr = null) { $tree = $this->scopeTree; $cur = $this->data; while ($parentLevels--!==0) { array_pop($tree); } while (($i = array_shift($tree)) !== null) { if (is_object($cur)) { $cur = $cur->$i; } else { $cur = $cur[$i]; } } if ($varstr!==null) { return $this->readVarInto($varstr, $cur); } else { return $cur; } } public function readVar($varstr) { if (is_array($varstr)===true) { $m = $varstr; unset($varstr); } else { if (strstr($varstr, '.') === false && strstr($varstr, '[') === false && strstr($varstr, '->') === false) { if ($varstr === 'dwoo') { return $this->globals; } elseif ($varstr === '__' || $varstr === '_root' ) { return $this->data; $varstr = substr($varstr, 6); } elseif ($varstr === '_' || $varstr === '_parent') { $varstr = '.'.$varstr; $tree = $this->scopeTree; $cur = $this->data; array_pop($tree); while (($i = array_shift($tree)) !== null) { if (is_object($cur)) { $cur = $cur->$i; } else { $cur = $cur[$i]; } } return $cur; } $cur = $this->scope; if (isset($cur[$varstr])) { return $cur[$varstr]; } else { return null; } } if (substr($varstr, 0, 1) === '.') { $varstr = 'dwoo'.$varstr; } preg_match_all('#(\[|->|\.)?([^.[\]-]+)\]?#i', $varstr, $m); } $i = $m[2][0]; if ($i === 'dwoo') { $cur = $this->globals; array_shift($m[2]); array_shift($m[1]); switch ($m[2][0]) { case 'get': $cur = $_GET; break; case 'post': $cur = $_POST; break; case 'session': $cur = $_SESSION; break; case 'cookies': case 'cookie': $cur = $_COOKIE; break; case 'server': $cur = $_SERVER; break; case 'env': $cur = $_ENV; break; case 'request': $cur = $_REQUEST; break; case 'const': array_shift($m[2]); if (defined($m[2][0])) { return constant($m[2][0]); } else { return null; } } if ($cur !== $this->globals) { array_shift($m[2]); array_shift($m[1]); } } elseif ($i === '__' || $i === '_root') { $cur = $this->data; array_shift($m[2]); array_shift($m[1]); } elseif ($i === '_' || $i === '_parent') { $tree = $this->scopeTree; $cur = $this->data; while (true) { array_pop($tree); array_shift($m[2]); array_shift($m[1]); if (current($m[2]) === '_' || current($m[2]) === '_parent') { continue; } while (($i = array_shift($tree)) !== null) { if (is_object($cur)) { $cur = $cur->$i; } else { $cur = $cur[$i]; } } break; } } else { $cur = $this->scope; } while (list($k, $sep) = each($m[1])) { if ($sep === '.' || $sep === '[' || $sep === '') { if ((is_array($cur) || $cur instanceof ArrayAccess) && isset($cur[$m[2][$k]])) { $cur = $cur[$m[2][$k]]; } else { return null; } } elseif ($sep === '->') { if (is_object($cur)) { $cur = $cur->$m[2][$k]; } else { return null; } } else { return null; } } return $cur; } public function assignInScope($value, $scope) { $tree =& $this->scopeTree; $data =& $this->data; if (!is_string($scope)) { return $this->triggerError('Assignments must be done into strings, ('.gettype($scope).') '.var_export($scope, true).' given', E_USER_ERROR); } if (strstr($scope, '.') === false && strstr($scope, '->') === false) { $this->scope[$scope] = $value; } else { preg_match_all('#(\[|->|\.)?([^.[\]-]+)\]?#i', $scope, $m); $cur =& $this->scope; $last = array(array_pop($m[1]), array_pop($m[2])); while (list($k, $sep) = each($m[1])) { if ($sep === '.' || $sep === '[' || $sep === '') { if (is_array($cur) === false) { $cur = array(); } $cur =& $cur[$m[2][$k]]; } elseif ($sep === '->') { if (is_object($cur) === false) { $cur = new stdClass; } $cur =& $cur->$m[2][$k]; } else { return false; } } if ($last[0] === '.' || $last[0] === '[' || $last[0] === '') { if (is_array($cur) === false) { $cur = array(); } $cur[$last[1]] = $value; } elseif ($last[0] === '->') { if (is_object($cur) === false) { $cur = new stdClass; } $cur->$last[1] = $value; } else { return false; } } } public function setScope($scope, $absolute = false) { $old = $this->scopeTree; if (is_string($scope)===true) { $scope = explode('.', $scope); } if ($absolute===true) { $this->scope =& $this->data; $this->scopeTree = array(); } while (($bit = array_shift($scope)) !== null) { if ($bit === '_' || $bit === '_parent') { array_pop($this->scopeTree); $this->scope =& $this->data; $cnt = count($this->scopeTree); for ($i=0;$i<$cnt;$i++) $this->scope =& $this->scope[$this->scopeTree[$i]]; } elseif ($bit === '__' || $bit === '_root') { $this->scope =& $this->data; $this->scopeTree = array(); } elseif (isset($this->scope[$bit])) { $this->scope =& $this->scope[$bit]; $this->scopeTree[] = $bit; } else { unset($this->scope); $this->scope = null; } } return $old; } public function getData() { return $this->data; } public function &getScope() { return $this->scope; } public function forceScope($scope) { return $this->setScope($scope, true); } } interface Dwoo_IPluginProxy { public function handles($name); public function getCode($name, $params); public function getCallback($name); public function getLoader($name); } interface Dwoo_ILoader { public function loadPlugin($class, $forceRehash = true); } class Dwoo_Loader implements Dwoo_ILoader { protected $paths = array(); protected $classPath = array(); protected $cacheDir; protected $corePluginDir; public function __construct($cacheDir) { $this->corePluginDir = DWOO_DIRECTORY . 'plugins'; $this->cacheDir = $cacheDir . DIRECTORY_SEPARATOR; $foo = @file_get_contents($this->cacheDir.'classpath.cache.d'.Dwoo::RELEASE_TAG.'.php'); if ($foo) { $this->classPath = unserialize($foo) + $this->classPath; } else { $this->rebuildClassPathCache($this->corePluginDir, $this->cacheDir.'classpath.cache.d'.Dwoo::RELEASE_TAG.'.php'); } } protected function rebuildClassPathCache($path, $cacheFile) { if ($cacheFile!==false) { $tmp = $this->classPath; $this->classPath = array(); } $list = glob($path.DIRECTORY_SEPARATOR.'*'); if (is_array($list)) { foreach ($list as $f) { if (is_dir($f)) { $this->rebuildClassPathCache($f, false); } else { $this->classPath[str_replace(array('function.','block.','modifier.','outputfilter.','filter.','prefilter.','postfilter.','pre.','post.','output.','shared.','helper.'), '', basename($f, '.php'))] = $f; } } } if ($cacheFile!==false) { if (!file_put_contents($cacheFile, serialize($this->classPath))) { throw new Dwoo_Exception('Could not write into '.$cacheFile.', either because the folder is not there (create it) or because of the chmod configuration (please ensure this directory is writable by php), alternatively you can change the directory used with $dwoo->setCompileDir() or provide a custom loader object with $dwoo->setLoader()'); } $this->classPath += $tmp; } } public function loadPlugin($class, $forceRehash = true) { if (!isset($this->classPath[$class]) || !(include $this->classPath[$class])) { if ($forceRehash) { $this->rebuildClassPathCache($this->corePluginDir, $this->cacheDir . 'classpath.cache.d'.Dwoo::RELEASE_TAG.'.php'); foreach ($this->paths as $path=>$file) { $this->rebuildClassPathCache($path, $file); } if (isset($this->classPath[$class])) { include $this->classPath[$class]; } else { throw new Dwoo_Exception('Plugin '.$class.' can not be found, maybe you forgot to bind it if it\'s a custom plugin ?', E_USER_NOTICE); } } else { throw new Dwoo_Exception('Plugin '.$class.' can not be found, maybe you forgot to bind it if it\'s a custom plugin ?', E_USER_NOTICE); } } } public function addDirectory($pluginDirectory) { $pluginDir = realpath($pluginDirectory); if (!$pluginDir) { throw new Dwoo_Exception('Plugin directory does not exist or can not be read : '.$pluginDirectory); } $cacheFile = $this->cacheDir . 'classpath-'.substr(strtr($pluginDir, '/\\:'.PATH_SEPARATOR, '----'), strlen($pluginDir) > 80 ? -80 : 0).'.d'.Dwoo::RELEASE_TAG.'.php'; $this->paths[$pluginDir] = $cacheFile; $foo = @file_get_contents($cacheFile); if ($foo) { $this->classPath = unserialize($foo) + $this->classPath; } else { $this->rebuildClassPathCache($pluginDir, $cacheFile); } } } class Dwoo_Exception extends Exception { } class Dwoo_Security_Policy { const PHP_ENCODE = 1; const PHP_REMOVE = 2; const PHP_ALLOW = 3; const CONST_DISALLOW = false; const CONST_ALLOW = true; protected $allowedPhpFunctions = array ( 'str_repeat', 'number_format', 'htmlentities', 'htmlspecialchars', 'long2ip', 'strlen', 'list', 'empty', 'count', 'sizeof', 'in_array', 'is_array', ); protected $allowedDirectories = array(); protected $phpHandling = self::PHP_REMOVE; protected $constHandling = self::CONST_DISALLOW; public function allowPhpFunction($func) { if (is_array($func)) foreach ($func as $fname) $this->allowedPhpFunctions[strtolower($fname)] = true; else $this->allowedPhpFunctions[strtolower($func)] = true; } public function disallowPhpFunction($func) { if (is_array($func)) foreach ($func as $fname) unset($this->allowedPhpFunctions[strtolower($fname)]); else unset($this->allowedPhpFunctions[strtolower($func)]); } public function getAllowedPhpFunctions() { return $this->allowedPhpFunctions; } public function allowDirectory($path) { if (is_array($path)) foreach ($path as $dir) $this->allowedDirectories[realpath($dir)] = true; else $this->allowedDirectories[realpath($path)] = true; } public function disallowDirectory($path) { if (is_array($path)) foreach ($path as $dir) unset($this->allowedDirectories[realpath($dir)]); else unset($this->allowedDirectories[realpath($path)]); } public function getAllowedDirectories() { return $this->allowedDirectories; } public function setPhpHandling($level = self::PHP_REMOVE) { $this->phpHandling = $level; } public function getPhpHandling() { return $this->phpHandling; } public function setConstantHandling($level = self::CONST_DISALLOW) { $this->constHandling = $level; } public function getConstantHandling() { return $this->constHandling; } } class Dwoo_Security_Exception extends Dwoo_Exception { } interface Dwoo_IElseable { } interface Dwoo_ICompilable { } interface Dwoo_ICompiler { public function compile(Dwoo $dwoo, Dwoo_ITemplate $template); public function setCustomPlugins(array $customPlugins); public function setSecurityPolicy(Dwoo_Security_Policy $policy = null); } interface Dwoo_IDataProvider { public function getData(); } interface Dwoo_ITemplate { public function getCacheTime(); public function setCacheTime($seconds = null); public function getCachedTemplate(Dwoo $dwoo); public function cache(Dwoo $dwoo, $output); public function clearCache(Dwoo $dwoo, $olderThan = -1); public function getCompiledTemplate(Dwoo $dwoo, Dwoo_ICompiler $compiler = null); public function getName(); public function getResourceName(); public function getResourceIdentifier(); public function getSource(); public function getUid(); public function getCompiler(); public static function templateFactory(Dwoo $dwoo, $resourceId, $cacheTime = null, $cacheId = null, $compileId = null, Dwoo_ITemplate $parentTemplate = null); } interface Dwoo_ICompilable_Block { } abstract class Dwoo_Plugin { protected $dwoo; public function __construct(Dwoo $dwoo) { $this->dwoo = $dwoo; } public static function paramsToAttributes(array $params, $delim = '\'') { if (isset($params['*'])) { $params = array_merge($params, $params['*']); unset($params['*']); } $out = ''; foreach ($params as $attr=>$val) { $out .= ' '.$attr.'='; if (trim($val, '"\'')=='' || $val=='null') { $out .= str_replace($delim, '\\'.$delim, '""'); } elseif (substr($val, 0, 1) === $delim && substr($val, -1) === $delim) { $out .= str_replace($delim, '\\'.$delim, '"'.substr($val, 1, -1).'"'); } else { $out .= str_replace($delim, '\\'.$delim, '"') . $delim . '.'.$val.'.' . $delim . str_replace($delim, '\\'.$delim, '"'); } } return ltrim($out); } } abstract class Dwoo_Block_Plugin extends Dwoo_Plugin { protected $buffer = ''; public function buffer($input) { $this->buffer .= $input; } public function end() { } public function process() { return $this->buffer; } public static function preProcessing(Dwoo_Compiler $compiler, array $params, $prepend, $append, $type) { return Dwoo_Compiler::PHP_OPEN.$prepend.'$this->addStack("'.$type.'", array('.Dwoo_Compiler::implode_r($compiler->getCompiledParams($params)).'));'.$append.Dwoo_Compiler::PHP_CLOSE; } public static function postProcessing(Dwoo_Compiler $compiler, array $params, $prepend, $append, $content) { return $content . Dwoo_Compiler::PHP_OPEN.$prepend.'$this->delStack();'.$append.Dwoo_Compiler::PHP_CLOSE; } } abstract class Dwoo_Filter { protected $dwoo; public function __construct(Dwoo $dwoo) { $this->dwoo = $dwoo; } abstract public function process($input); } abstract class Dwoo_Processor { protected $compiler; public function __construct(Dwoo_Compiler $compiler) { $this->compiler = $compiler; } abstract public function process($input); } class Dwoo_Template_String implements Dwoo_ITemplate { protected $name; protected $compileId; protected $cacheId; protected $cacheTime; protected $compilationEnforced; protected static $cache = array('cached'=>array(), 'compiled'=>array()); protected $compiler; protected $chmod = 0777; public function __construct($templateString, $cacheTime = null, $cacheId = null, $compileId = null) { $this->template = $templateString; if (function_exists('hash')) { $this->name = hash('md4', $templateString); } else { $this->name = md5($templateString); } $this->cacheTime = $cacheTime; if ($compileId !== null) { $this->compileId = str_replace('../', '__', strtr($compileId, '\\%?=!:;'.PATH_SEPARATOR, '/-------')); } if ($cacheId !== null) { $this->cacheId = str_replace('../', '__', strtr($cacheId, '\\%?=!:;'.PATH_SEPARATOR, '/-------')); } } public function getCacheTime() { return $this->cacheTime; } public function setCacheTime($seconds = null) { $this->cacheTime = $seconds; } public function getChmod() { return $this->chmod; } public function setChmod($mask = null) { $this->chmod = $mask; } public function getName() { return $this->name; } public function getResourceName() { return 'string'; } public function getResourceIdentifier() { return false; } public function getSource() { return $this->template; } public function getUid() { return $this->name; } public function getCompiler() { return $this->compiler; } public function forceCompilation() { $this->compilationEnforced = true; } public function getCachedTemplate(Dwoo $dwoo) { if ($this->cacheTime !== null) { $cacheLength = $this->cacheTime; } else { $cacheLength = $dwoo->getCacheTime(); } if ($cacheLength === 0) { return false; } $cachedFile = $this->getCacheFilename($dwoo); if (isset(self::$cache['cached'][$this->cacheId]) === true && file_exists($cachedFile)) { return $cachedFile; } elseif ($this->compilationEnforced !== true && file_exists($cachedFile) && ($cacheLength === -1 || filemtime($cachedFile) > ($_SERVER['REQUEST_TIME'] - $cacheLength))) { self::$cache['cached'][$this->cacheId] = true; return $cachedFile; } else { return true; } } public function cache(Dwoo $dwoo, $output) { $cacheDir = $dwoo->getCacheDir(); $cachedFile = $this->getCacheFilename($dwoo); $temp = tempnam($cacheDir, 'temp'); if (!($file = @fopen($temp, 'wb'))) { $temp = $cacheDir . DIRECTORY_SEPARATOR . uniqid('temp'); if (!($file = @fopen($temp, 'wb'))) { trigger_error('Error writing temporary file \''.$temp.'\'', E_USER_WARNING); return false; } } fwrite($file, $output); fclose($file); $this->makeDirectory(dirname($cachedFile)); if (!@rename($temp, $cachedFile)) { @unlink($cachedFile); @rename($temp, $cachedFile); } if ($this->chmod !== null) { chmod($cachedFile, $this->chmod); } self::$cache['cached'][$this->cacheId] = true; return $cachedFile; } public function clearCache(Dwoo $dwoo, $olderThan = -1) { $cachedFile = $this->getCacheFilename($dwoo); return !file_exists($cachedFile) || (filectime($cachedFile) < (time() - $olderThan) && unlink($cachedFile)); } public function getCompiledTemplate(Dwoo $dwoo, Dwoo_ICompiler $compiler = null) { $compiledFile = $this->getCompiledFilename($dwoo); if ($this->compilationEnforced !== true && isset(self::$cache['compiled'][$this->compileId]) === true) { } elseif ($this->compilationEnforced !== true && $this->isValidCompiledFile($compiledFile)) { self::$cache['compiled'][$this->compileId] = true; } else { $this->compilationEnforced = false; if ($compiler === null) { $compiler = $dwoo->getDefaultCompilerFactory($this->getResourceName()); if ($compiler === null || $compiler === array('Dwoo_Compiler', 'compilerFactory')) { if (class_exists('Dwoo_Compiler', false) === false) { include DWOO_DIRECTORY . 'Dwoo/Compiler.php'; } $compiler = Dwoo_Compiler::compilerFactory(); } else { $compiler = call_user_func($compiler); } } $this->compiler = $compiler; $compiler->setCustomPlugins($dwoo->getCustomPlugins()); $compiler->setSecurityPolicy($dwoo->getSecurityPolicy()); $this->makeDirectory(dirname($compiledFile)); file_put_contents($compiledFile, $compiler->compile($dwoo, $this)); if ($this->chmod !== null) { chmod($compiledFile, $this->chmod); } self::$cache['compiled'][$this->compileId] = true; } return $compiledFile; } protected function isValidCompiledFile($file) { return file_exists($file); } public static function templateFactory(Dwoo $dwoo, $resourceId, $cacheTime = null, $cacheId = null, $compileId = null, Dwoo_ITemplate $parentTemplate = null) { return new self($resourceId, $cacheTime, $cacheId, $compileId); } protected function getCompiledFilename(Dwoo $dwoo) { if ($this->compileId===null) { $this->compileId = $this->name; } return $dwoo->getCompileDir() . $this->compileId.'.d'.Dwoo::RELEASE_TAG.'.php'; } protected function getCacheFilename(Dwoo $dwoo) { if ($this->cacheId === null) { if (isset($_SERVER['REQUEST_URI']) === true) { $cacheId = $_SERVER['REQUEST_URI']; } elseif (isset($_SERVER['SCRIPT_FILENAME']) && isset($_SERVER['argv'])) { $cacheId = $_SERVER['SCRIPT_FILENAME'].'-'.implode('-', $_SERVER['argv']); } else { $cacheId = ''; } $this->getCompiledFilename($dwoo); $this->cacheId = str_replace('../', '__', $this->compileId . strtr($cacheId, '\\%?=!:;'.PATH_SEPARATOR, '/-------')); } return $dwoo->getCacheDir() . $this->cacheId.'.html'; } protected function makeDirectory($path) { if (is_dir($path) === true) { return; } if ($this->chmod !== null) { mkdir($path, $this->chmod, true); } else { mkdir($path, 0777, true); } } } class Dwoo_Template_File extends Dwoo_Template_String { protected $file; protected $includePath = null; protected $resolvedPath = null; public function __construct($file, $cacheTime = null, $cacheId = null, $compileId = null, $includePath = null) { $this->file = $file; $this->name = basename($file); $this->cacheTime = $cacheTime; if ($compileId !== null) { $this->compileId = str_replace('../', '__', strtr($compileId, '\\%?=!:;'.PATH_SEPARATOR, '/-------')); } if ($cacheId !== null) { $this->cacheId = str_replace('../', '__', strtr($cacheId, '\\%?=!:;'.PATH_SEPARATOR, '/-------')); } if (is_string($includePath)) { $this->includePath = array($includePath); } elseif (is_array($includePath)) { $this->includePath = $includePath; } } public function setIncludePath($paths) { if (is_array($paths) === false) { $paths = array($paths); } $this->includePath = $paths; $this->resolvedPath = null; } public function getIncludePath() { return $this->includePath; } protected function isValidCompiledFile($file) { return parent::isValidCompiledFile($file) && (int)$this->getUid() <= filemtime($file); } public function getSource() { return file_get_contents($this->getResourceIdentifier()); } public function getResourceName() { return 'file'; } public function getResourceIdentifier() { if ($this->resolvedPath !== null) { return $this->resolvedPath; } elseif ($this->includePath === null) { return $this->file; } else { foreach ($this->includePath as $path) { if (file_exists($path.DIRECTORY_SEPARATOR.$this->file) === true) { $this->resolvedPath = $path . DIRECTORY_SEPARATOR . $this->file; return $this->resolvedPath; } } throw new Dwoo_Exception('Template "'.$this->file.'" could not be found in any of your include path(s)'); } } public function getUid() { return (string) filemtime($this->getResourceIdentifier()); } public static function templateFactory(Dwoo $dwoo, $resourceId, $cacheTime = null, $cacheId = null, $compileId = null, Dwoo_ITemplate $parentTemplate = null) { if (DIRECTORY_SEPARATOR === '\\') { $resourceId = str_replace(array("\t", "\n", "\r", "\f", "\v"), array('\\t', '\\n', '\\r', '\\f', '\\v'), $resourceId); } $resourceId = strtr($resourceId, '\\', '/'); $includePath = null; if (file_exists($resourceId) === false) { if ($parentTemplate === null) { $parentTemplate = $dwoo->getTemplate(); } if ($parentTemplate instanceof Dwoo_Template_File) { if ($includePath = $parentTemplate->getIncludePath()) { if (strstr($resourceId, '../')) { throw new Dwoo_Exception('When using an include path you can not reference a template into a parent directory (using ../)'); } } else { $resourceId = dirname($parentTemplate->getResourceIdentifier()).DIRECTORY_SEPARATOR.$resourceId; if (file_exists($resourceId) === false) { return null; } } } else { return null; } } if ($policy = $dwoo->getSecurityPolicy()) { while (true) { if (preg_match('{^([a-z]+?)://}i', $resourceId)) { throw new Dwoo_Security_Exception('The security policy prevents you to read files from external sources : '.$resourceId.'.'); } if ($includePath) { break; } $resourceId = realpath($resourceId); $dirs = $policy->getAllowedDirectories(); foreach ($dirs as $dir=>$dummy) { if (strpos($resourceId, $dir) === 0) { break 2; } } throw new Dwoo_Security_Exception('The security policy prevents you to read '.$resourceId.''); } } return new Dwoo_Template_File($resourceId, $cacheTime, $cacheId, $compileId, $includePath); } protected function getCompiledFilename(Dwoo $dwoo) { if ($this->compileId===null) { $this->compileId = str_replace('../', '__', strtr($this->getResourceIdentifier(), '\\:', '/-')); } return $dwoo->getCompileDir() . $this->compileId.'.d'.Dwoo::RELEASE_TAG.'.php'; } } class Dwoo_Data implements Dwoo_IDataProvider { protected $data = array(); public function getData() { return $this->data; } public function clear($name = null) { if ($name === null) { $this->data = array(); } elseif (is_array($name)) { foreach ($name as $index) unset($this->data[$index]); } else { unset($this->data[$name]); } } public function setData(array $data) { $this->data = $data; } public function mergeData(array $data) { $args = func_get_args(); while (list(,$v) = each($args)) { if (is_array($v)) { $this->data = array_merge($this->data, $v); } } } public function assign($name, $val = null) { if (is_array($name)) { reset($name); while (list($k,$v) = each($name)) $this->data[$k] = $v; } else { $this->data[$name] = $val; } } public function __set($name, $value) { $this->assign($name, $value); } public function assignByRef($name, &$val) { $this->data[$name] =& $val; } public function append($name, $val = null, $merge = false) { if (is_array($name)) { foreach ($name as $key=>$val) { if (isset($this->data[$key]) && !is_array($this->data[$key])) { settype($this->data[$key], 'array'); } if ($merge === true && is_array($val)) { $this->data[$key] = $val + $this->data[$key]; } else { $this->data[$key][] = $val; } } } elseif ($val !== null) { if (isset($this->data[$name]) && !is_array($this->data[$name])) { settype($this->data[$name], 'array'); } if ($merge === true && is_array($val)) { $this->data[$name] = $val + $this->data[$name]; } else { $this->data[$name][] = $val; } } } public function appendByRef($name, &$val, $merge = false) { if (isset($this->data[$name]) && !is_array($this->data[$name])) { settype($this->data[$name], 'array'); } if ($merge === true && is_array($val)) { foreach ($val as $key => &$val) { $this->data[$name][$key] =& $val; } } else { $this->data[$name][] =& $val; } } public function isAssigned($name) { return isset($this->data[$name]); } public function __isset($name) { return isset($this->data[$name]); } public function unassign($name) { unset($this->data[$name]); } public function __unset($name) { unset($this->data[$name]); } public function get($name) { return $this->__get($name); } public function __get($name) { if (isset($this->data[$name])) { return $this->data[$name]; } else { throw new Dwoo_Exception('Tried to read a value that was not assigned yet : "'.$name.'"'); } } } \ No newline at end of file diff --git a/lib/dwoo/Dwoo/Compilation/Exception.php b/lib/dwoo/Dwoo/Compilation/Exception.php new file mode 100644 index 0000000..180b51f --- /dev/null +++ b/lib/dwoo/Dwoo/Compilation/Exception.php @@ -0,0 +1,38 @@ + + * @copyright Copyright (c) 2008, Jordi Boggiano + * @license http://dwoo.org/LICENSE Modified BSD License + * @link http://dwoo.org/ + * @version 1.0.0 + * @date 2008-10-23 + * @package Dwoo + */ +class Dwoo_Compilation_Exception extends Dwoo_Exception +{ + protected $compiler; + protected $template; + + public function __construct(Dwoo_Compiler $compiler, $message) + { + $this->compiler = $compiler; + $this->template = $compiler->getDwoo()->getTemplate(); + parent::__construct('Compilation error at line '.$compiler->getLine().' in "'.$this->template->getResourceName().':'.$this->template->getResourceIdentifier().'" : '.$message); + } + + public function getCompiler() + { + return $this->compiler; + } + + public function getTemplate() + { + return $this->template; + } +} diff --git a/lib/dwoo/Dwoo/Compiler.php b/lib/dwoo/Dwoo/Compiler.php new file mode 100644 index 0000000..c53bfcd --- /dev/null +++ b/lib/dwoo/Dwoo/Compiler.php @@ -0,0 +1,2854 @@ + + * @copyright Copyright (c) 2008, Jordi Boggiano + * @license http://dwoo.org/LICENSE Modified BSD License + * @link http://dwoo.org/ + * @version 1.0.1 + * @date 2008-12-24 + * @package Dwoo + */ +class Dwoo_Compiler implements Dwoo_ICompiler +{ + /** + * constant that represents a php opening tag + * + * use it in case it needs to be adjusted + * + * @var string + */ + const PHP_OPEN = ""; + + /** + * boolean flag to enable or disable debugging output + * + * @var bool + */ + public $debug = false; + + /** + * left script delimiter + * + * @var string + */ + protected $ld = '{'; + + /** + * left script delimiter with escaped regex meta characters + * + * @var string + */ + protected $ldr = '\\{'; + + /** + * right script delimiter + * + * @var string + */ + protected $rd = '}'; + + /** + * right script delimiter with escaped regex meta characters + * + * @var string + */ + protected $rdr = '\\}'; + + /** + * defines whether the nested comments should be parsed as nested or not + * + * defaults to false (classic block comment parsing as in all languages) + * + * @var bool + */ + protected $allowNestedComments = false; + + /** + * defines whether opening and closing tags can contain spaces before valid data or not + * + * turn to true if you want to be sloppy with the syntax, but when set to false it allows + * to skip javascript and css tags as long as they are in the form "{ something", which is + * nice. default is false. + * + * @var bool + */ + protected $allowLooseOpenings = false; + + /** + * defines whether the compiler will automatically html-escape variables or not + * + * default is false + * + * @var bool + */ + protected $autoEscape = false; + + /** + * security policy object + * + * @var Dwoo_Security_Policy + */ + protected $securityPolicy; + + /** + * stores the custom plugins registered with this compiler + * + * @var array + */ + protected $customPlugins = array(); + + /** + * stores the pre- and post-processors callbacks + * + * @var array + */ + protected $processors = array('pre'=>array(), 'post'=>array()); + + /** + * stores a list of plugins that are used in the currently compiled + * template, and that are not compilable. these plugins will be loaded + * during the template's runtime if required. + * + * it is a 1D array formatted as key:pluginName value:pluginType + * + * @var array + */ + protected $usedPlugins; + + /** + * stores the template undergoing compilation + * + * @var string + */ + protected $template; + + /** + * stores the current pointer position inside the template + * + * @var int + */ + protected $pointer; + + /** + * stores the data within which the scope moves + * + * @var array + */ + protected $data; + + /** + * variable scope of the compiler, set to null if + * it can not be resolved to a static string (i.e. if some + * plugin defines a new scope based on a variable array key) + * + * @var mixed + */ + protected $scope; + + /** + * variable scope tree, that allows to rebuild the current + * scope if required, i.e. when going to a parent level + * + * @var array + */ + protected $scopeTree; + + /** + * block plugins stack, accessible through some methods + * + * @see findBlock + * @see getCurrentBlock + * @see addBlock + * @see addCustomBlock + * @see injectBlock + * @see removeBlock + * @see removeTopBlock + * + * @var array + */ + protected $stack = array(); + + /** + * current block at the top of the block plugins stack, + * accessible through getCurrentBlock + * + * @see getCurrentBlock + * + * @var Dwoo_Block_Plugin + */ + protected $curBlock; + + /** + * current dwoo object that uses this compiler, or null + * + * @var Dwoo + */ + protected $dwoo; + + /** + * holds an instance of this class, used by getInstance when you don't + * provide a custom compiler in order to save resources + * + * @var Dwoo_Compiler + */ + protected static $instance; + + /** + * sets the delimiters to use in the templates + * + * delimiters can be multi-character strings but should not be one of those as they will + * make it very hard to work with templates or might even break the compiler entirely : "\", "$", "|", ":" and finally "#" only if you intend to use config-vars with the #var# syntax. + * + * @param string $left left delimiter + * @param string $right right delimiter + */ + public function setDelimiters($left, $right) + { + $this->ld = $left; + $this->rd = $right; + $this->ldr = preg_quote($left, '/'); + $this->rdr = preg_quote($right, '/'); + } + + /** + * returns the left and right template delimiters + * + * @return array containing the left and the right delimiters + */ + public function getDelimiters() + { + return array($this->ld, $this->rd); + } + + /** + * sets the way to handle nested comments, if set to true + * {* foo {* some other *} comment *} will be stripped correctly. + * + * if false it will remove {* foo {* some other *} and leave "comment *}" alone, + * this is the default behavior + * + * @param bool $allow allow nested comments or not, defaults to true (but the default internal value is false) + */ + public function setNestedCommentsHandling($allow = true) { + $this->allowNestedComments = (bool) $allow; + } + + /** + * returns the nested comments handling setting + * + * @see setNestedCommentsHandling + * @return bool true if nested comments are allowed + */ + public function getNestedCommentsHandling() { + return $this->allowNestedComments; + } + + /** + * sets the tag openings handling strictness, if set to true, template tags can + * contain spaces before the first function/string/variable such as { $foo} is valid. + * + * if set to false (default setting), { $foo} is invalid but that is however a good thing + * as it allows css (i.e. #foo { color:red; }) to be parsed silently without triggering + * an error, same goes for javascript. + * + * @param bool $allow true to allow loose handling, false to restore default setting + */ + public function setLooseOpeningHandling($allow = false) + { + $this->allowLooseOpenings = (bool) $allow; + } + + /** + * returns the tag openings handling strictness setting + * + * @see setLooseOpeningHandling + * @return bool true if loose tags are allowed + */ + public function getLooseOpeningHandling() + { + return $this->allowLooseOpenings; + } + + /** + * changes the auto escape setting + * + * if enabled, the compiler will automatically html-escape variables, + * unless they are passed through the safe function such as {$var|safe} + * or {safe $var} + * + * default setting is disabled/false + * + * @param bool $enabled set to true to enable, false to disable + */ + public function setAutoEscape($enabled) + { + $this->autoEscape = (bool) $enabled; + } + + /** + * returns the auto escape setting + * + * default setting is disabled/false + * + * @return bool + */ + public function getAutoEscape() + { + return $this->autoEscape; + } + + /** + * adds a preprocessor to the compiler, it will be called + * before the template is compiled + * + * @param mixed $callback either a valid callback to the preprocessor or a simple name if the autoload is set to true + * @param bool $autoload if set to true, the preprocessor is auto-loaded from one of the plugin directories, else you must provide a valid callback + */ + public function addPreProcessor($callback, $autoload = false) + { + if ($autoload) { + $name = str_replace('Dwoo_Processor_', '', $callback); + $class = 'Dwoo_Processor_'.$name; + + if (class_exists($class, false)) { + $callback = array(new $class($this), 'process'); + } elseif (function_exists($class)) { + $callback = $class; + } else { + $callback = array('autoload'=>true, 'class'=>$class, 'name'=>$name); + } + + $this->processors['pre'][] = $callback; + } else { + $this->processors['pre'][] = $callback; + } + } + + /** + * removes a preprocessor from the compiler + * + * @param mixed $callback either a valid callback to the preprocessor or a simple name if it was autoloaded + */ + public function removePreProcessor($callback) + { + if (($index = array_search($callback, $this->processors['pre'], true)) !== false) { + unset($this->processors['pre'][$index]); + } elseif (($index = array_search('Dwoo_Processor_'.str_replace('Dwoo_Processor_', '', $callback), $this->processors['pre'], true)) !== false) { + unset($this->processors['pre'][$index]); + } else { + $class = 'Dwoo_Processor_' . str_replace('Dwoo_Processor_', '', $callback); + foreach ($this->processors['pre'] as $index=>$proc) { + if (is_array($proc) && ($proc[0] instanceof $class) || (isset($proc['class']) && $proc['class'] == $class)) { + unset($this->processors['pre'][$index]); + break; + } + } + } + } + + /** + * adds a postprocessor to the compiler, it will be called + * before the template is compiled + * + * @param mixed $callback either a valid callback to the postprocessor or a simple name if the autoload is set to true + * @param bool $autoload if set to true, the postprocessor is auto-loaded from one of the plugin directories, else you must provide a valid callback + */ + public function addPostProcessor($callback, $autoload = false) + { + if ($autoload) { + $name = str_replace('Dwoo_Processor_', '', $callback); + $class = 'Dwoo_Processor_'.$name; + + if (class_exists($class, false)) { + $callback = array(new $class($this), 'process'); + } elseif (function_exists($class)) { + $callback = $class; + } else { + $callback = array('autoload'=>true, 'class'=>$class, 'name'=>$name); + } + + $this->processors['post'][] = $callback; + } else { + $this->processors['post'][] = $callback; + } + } + + /** + * removes a postprocessor from the compiler + * + * @param mixed $callback either a valid callback to the postprocessor or a simple name if it was autoloaded + */ + public function removePostProcessor($callback) + { + if (($index = array_search($callback, $this->processors['post'], true)) !== false) { + unset($this->processors['post'][$index]); + } elseif (($index = array_search('Dwoo_Processor_'.str_replace('Dwoo_Processor_', '', $callback), $this->processors['post'], true)) !== false) { + unset($this->processors['post'][$index]); + } else { + $class = 'Dwoo_Processor_' . str_replace('Dwoo_Processor_', '', $callback); + foreach ($this->processors['post'] as $index=>$proc) { + if (is_array($proc) && ($proc[0] instanceof $class) || (isset($proc['class']) && $proc['class'] == $class)) { + unset($this->processors['post'][$index]); + break; + } + } + } + } + + /** + * internal function to autoload processors at runtime if required + * + * @param string $class the class/function name + * @param string $name the plugin name (without Dwoo_Plugin_ prefix) + */ + protected function loadProcessor($class, $name) + { + if (!class_exists($class, false) && !function_exists($class)) { + try { + $this->dwoo->getLoader()->loadPlugin($name); + } catch (Dwoo_Exception $e) { + throw new Dwoo_Exception('Processor '.$name.' could not be found in your plugin directories, please ensure it is in a file named '.$name.'.php in the plugin directory'); + } + } + + if (class_exists($class, false)) { + return array(new $class($this), 'process'); + } + + if (function_exists($class)) { + return $class; + } + + throw new Dwoo_Exception('Wrong processor name, when using autoload the processor must be in one of your plugin dir as "name.php" containg a class or function named "Dwoo_Processor_name"'); + } + + /** + * adds the custom plugins loaded into Dwoo to the compiler so it can load them + * + * @see Dwoo::addPlugin + * @param array $customPlugins an array of custom plugins + */ + public function setCustomPlugins(array $customPlugins) + { + $this->customPlugins = $customPlugins; + } + + /** + * sets the security policy object to enforce some php security settings + * + * use this if untrusted persons can modify templates, + * set it on the Dwoo object as it will be passed onto the compiler automatically + * + * @param Dwoo_Security_Policy $policy the security policy object + */ + public function setSecurityPolicy(Dwoo_Security_Policy $policy = null) + { + $this->securityPolicy = $policy; + } + + /** + * returns the current security policy object or null by default + * + * @return Dwoo_Security_Policy|null the security policy object if any + */ + public function getSecurityPolicy() + { + return $this->securityPolicy; + } + + /** + * sets the pointer position + * + * @param int $position the new pointer position + * @param bool $isOffset if set to true, the position acts as an offset and not an absolute position + */ + public function setPointer($position, $isOffset = false) + { + if ($isOffset) { + $this->pointer += $position; + } else { + $this->pointer = $position; + } + } + + /** + * returns the current pointer position, only available during compilation of a template + * + * @return int + */ + public function getPointer() + { + return $this->pointer; + } + + /** + * sets the line number + * + * @param int $number the new line number + * @param bool $isOffset if set to true, the position acts as an offset and not an absolute position + */ + public function setLine($number, $isOffset = false) + { + if ($isOffset) { + $this->line += $number; + } else { + $this->line = $number; + } + } + + /** + * returns the current line number, only available during compilation of a template + * + * @return int + */ + public function getLine() + { + return $this->line; + } + + /** + * returns the dwoo object that initiated this template compilation, only available during compilation of a template + * + * @return Dwoo + */ + public function getDwoo() + { + return $this->dwoo; + } + + /** + * overwrites the template that is being compiled + * + * @param string $newSource the template source that must replace the current one + * @param bool $fromPointer if set to true, only the source from the current pointer position is replaced + * @return string the template or partial template + */ + public function setTemplateSource($newSource, $fromPointer = false) + { + if ($fromPointer === true) { + $this->templateSource = substr($this->templateSource, 0, $this->pointer) . $newSource; + } else { + $this->templateSource = $newSource; + } + } + + /** + * returns the template that is being compiled + * + * @param mixed $fromPointer if set to true, only the source from the current pointer + * position is returned, if a number is given it overrides the current pointer + * @return string the template or partial template + */ + public function getTemplateSource($fromPointer = false) + { + if ($fromPointer === true) { + return substr($this->templateSource, $this->pointer); + } elseif (is_numeric($fromPointer)) { + return substr($this->templateSource, $fromPointer); + } else { + return $this->templateSource; + } + } + + /** + * compiles the provided string down to php code + * + * @param string $tpl the template to compile + * @return string a compiled php string + */ + public function compile(Dwoo $dwoo, Dwoo_ITemplate $template) + { + // init vars + $tpl = $template->getSource(); + $ptr = 0; + $this->dwoo = $dwoo; + $this->template = $template; + $this->templateSource =& $tpl; + $this->pointer =& $ptr; + + while (true) { + // if pointer is at the beginning, reset everything, that allows a plugin to externally reset the compiler if everything must be reparsed + if ($ptr===0) { + // resets variables + $this->usedPlugins = array(); + $this->data = array(); + $this->scope =& $this->data; + $this->scopeTree = array(); + $this->stack = array(); + $this->line = 1; + // add top level block + $compiled = $this->addBlock('topLevelBlock', array(), 0); + $this->stack[0]['buffer'] = ''; + + if ($this->debug) echo 'COMPILER INIT'.print_r(htmlentities($tpl), true).'
'; + $lines = preg_split('{\r\n|\n|'; + + $this->template = $this->dwoo = null; + $tpl = null; + + return $output; + } + + /** + * adds compiled content to the current block + * + * @param string $content the content to push + * @param int $lineCount newlines count in content, optional + */ + public function push($content, $lineCount = null) + { + if ($lineCount === null) { + $lineCount = substr_count($content, "\n"); + } + + if ($this->curBlock['buffer'] === null && count($this->stack) > 1) { + // buffer is not initialized yet (the block has just been created) + $this->stack[count($this->stack)-2]['buffer'] .= (string) $content; + $this->curBlock['buffer'] = ''; + } else { + if (!isset($this->curBlock['buffer'])) { + throw new Dwoo_Compilation_Exception($this, 'The template has been closed too early, you probably have an extra block-closing tag somewhere'); + } + // append current content to current block's buffer + $this->curBlock['buffer'] .= (string) $content; + } + $this->line += $lineCount; + } + + /** + * sets the scope + * + * set to null if the scope becomes "unstable" (i.e. too variable or unknown) so that + * variables are compiled in a more evaluative way than just $this->scope['key'] + * + * @param mixed $scope a string i.e. "level1.level2" or an array i.e. array("level1", "level2") + * @param bool $absolute if true, the scope is set from the top level scope and not from the current scope + * @return array the current scope tree + */ + public function setScope($scope, $absolute = false) + { + $old = $this->scopeTree; + + if ($scope===null) { + unset($this->scope); + $this->scope = null; + } + + if (is_array($scope)===false) { + $scope = explode('.', $scope); + } + + if ($absolute===true) { + $this->scope =& $this->data; + $this->scopeTree = array(); + } + + while (($bit = array_shift($scope)) !== null) { + if ($bit === '_parent' || $bit === '_') { + array_pop($this->scopeTree); + reset($this->scopeTree); + $this->scope =& $this->data; + $cnt = count($this->scopeTree); + for ($i=0;$i<$cnt;$i++) + $this->scope =& $this->scope[$this->scopeTree[$i]]; + } elseif ($bit === '_root' || $bit === '__') { + $this->scope =& $this->data; + $this->scopeTree = array(); + } elseif (isset($this->scope[$bit])) { + $this->scope =& $this->scope[$bit]; + $this->scopeTree[] = $bit; + } else { + $this->scope[$bit] = array(); + $this->scope =& $this->scope[$bit]; + $this->scopeTree[] = $bit; + } + } + + return $old; + } + + /** + * forces an absolute scope + * + * @deprecated + * @param mixed $scope a scope as a string or array + * @return array the current scope tree + */ + public function forceScope($scope) + { + return $this->setScope($scope, true); + } + + /** + * adds a block to the top of the block stack + * + * @param string $type block type (name) + * @param array $params the parameters array + * @param int $paramtype the parameters type (see mapParams), 0, 1 or 2 + * @return string the preProcessing() method's output + */ + public function addBlock($type, array $params, $paramtype) + { + $class = 'Dwoo_Plugin_'.$type; + if (class_exists($class, false) === false) { + $this->dwoo->getLoader()->loadPlugin($type); + } + + $params = $this->mapParams($params, array($class, 'init'), $paramtype); + + $this->stack[] = array('type' => $type, 'params' => $params, 'custom' => false, 'class' => $class, 'buffer' => null); + $this->curBlock =& $this->stack[count($this->stack)-1]; + return call_user_func(array($class,'preProcessing'), $this, $params, '', '', $type); + } + + /** + * adds a custom block to the top of the block stack + * + * @param string $type block type (name) + * @param array $params the parameters array + * @param int $paramtype the parameters type (see mapParams), 0, 1 or 2 + * @return string the preProcessing() method's output + */ + public function addCustomBlock($type, array $params, $paramtype) + { + $callback = $this->customPlugins[$type]['callback']; + if (is_array($callback)) { + $class = is_object($callback[0]) ? get_class($callback[0]) : $callback[0]; + } else { + $class = $callback; + } + + $params = $this->mapParams($params, array($class, 'init'), $paramtype); + + $this->stack[] = array('type' => $type, 'params' => $params, 'custom' => true, 'class' => $class, 'buffer' => null); + $this->curBlock =& $this->stack[count($this->stack)-1]; + return call_user_func(array($class,'preProcessing'), $this, $params, '', '', $type); + } + + /** + * injects a block at the top of the plugin stack without calling its preProcessing method + * + * used by {else} blocks to re-add themselves after having closed everything up to their parent + * + * @param string $type block type (name) + * @param array $params parameters array + */ + public function injectBlock($type, array $params) + { + $class = 'Dwoo_Plugin_'.$type; + if (class_exists($class, false) === false) { + $this->dwoo->getLoader()->loadPlugin($type); + } + $this->stack[] = array('type' => $type, 'params' => $params, 'custom' => false, 'class' => $class, 'buffer' => null); + $this->curBlock =& $this->stack[count($this->stack)-1]; + } + + /** + * removes the closest-to-top block of the given type and all other + * blocks encountered while going down the block stack + * + * @param string $type block type (name) + * @return string the output of all postProcessing() method's return values of the closed blocks + */ + public function removeBlock($type) + { + $output = ''; + + $pluginType = $this->getPluginType($type); + if ($pluginType & Dwoo::SMARTY_BLOCK) { + $type = 'smartyinterface'; + } + while (true) { + while ($top = array_pop($this->stack)) { + if ($top['custom']) { + $class = $top['class']; + } else { + $class = 'Dwoo_Plugin_'.$top['type']; + } + if (count($this->stack)) { + $this->curBlock =& $this->stack[count($this->stack)-1]; + $this->push(call_user_func(array($class, 'postProcessing'), $this, $top['params'], '', '', $top['buffer']), 0); + } else { + $null = null; + $this->curBlock =& $null; + $output = call_user_func(array($class, 'postProcessing'), $this, $top['params'], '', '', $top['buffer']); + } + + if ($top['type'] === $type) { + break 2; + } + } + + throw new Dwoo_Compilation_Exception($this, 'Syntax malformation, a block of type "'.$type.'" was closed but was not opened'); + break; + } + + return $output; + } + + /** + * returns a reference to the first block of the given type encountered and + * optionally closes all blocks until it finds it + * + * this is mainly used by {else} plugins to close everything that was opened + * between their parent and themselves + * + * @param string $type the block type (name) + * @param bool $closeAlong whether to close all blocks encountered while going down the block stack or not + * @return &array the array is as such: array('type'=>pluginName, 'params'=>parameter array, + * 'custom'=>bool defining whether it's a custom plugin or not, for internal use) + */ + public function &findBlock($type, $closeAlong = false) + { + if ($closeAlong===true) { + while ($b = end($this->stack)) { + if ($b['type']===$type) { + return $this->stack[key($this->stack)]; + } + $this->push($this->removeTopBlock(), 0); + } + } else { + end($this->stack); + while ($b = current($this->stack)) { + if ($b['type']===$type) { + return $this->stack[key($this->stack)]; + } + prev($this->stack); + } + } + + throw new Dwoo_Compilation_Exception($this, 'A parent block of type "'.$type.'" is required and can not be found'); + } + + /** + * returns a reference to the current block array + * + * @return &array the array is as such: array('type'=>pluginName, 'params'=>parameter array, + * 'custom'=>bool defining whether it's a custom plugin or not, for internal use) + */ + public function &getCurrentBlock() + { + return $this->curBlock; + } + + /** + * removes the block at the top of the stack and calls its postProcessing() method + * + * @return string the postProcessing() method's output + */ + public function removeTopBlock() + { + $o = array_pop($this->stack); + if ($o === null) { + throw new Dwoo_Compilation_Exception($this, 'Syntax malformation, a block of unknown type was closed but was not opened.'); + } + if ($o['custom']) { + $class = $o['class']; + } else { + $class = 'Dwoo_Plugin_'.$o['type']; + } + + $this->curBlock =& $this->stack[count($this->stack)-1]; + + return call_user_func(array($class, 'postProcessing'), $this, $o['params'], '', '', $o['buffer']); + } + + /** + * returns the compiled parameters (for example a variable's compiled parameter will be "$this->scope['key']") out of the given parameter array + * + * @param array $params parameter array + * @return array filtered parameters + */ + public function getCompiledParams(array $params) + { + foreach ($params as $k=>$p) { + if (is_array($p)) { + $params[$k] = $p[0]; + } + } + return $params; + } + + /** + * returns the real parameters (for example a variable's real parameter will be its key, etc) out of the given parameter array + * + * @param array $params parameter array + * @return array filtered parameters + */ + public function getRealParams(array $params) + { + foreach ($params as $k=>$p) { + if (is_array($p)) { + $params[$k] = $p[1]; + } + } + return $params; + } + + /** + * entry point of the parser, it redirects calls to other parse* functions + * + * @param string $in the string within which we must parse something + * @param int $from the starting offset of the parsed area + * @param int $to the ending offset of the parsed area + * @param mixed $parsingParams must be an array if we are parsing a function or modifier's parameters, or false by default + * @param string $curBlock the current parser-block being processed + * @param mixed $pointer a reference to a pointer that will be increased by the amount of characters parsed, or null by default + * @return string parsed values + */ + protected function parse($in, $from, $to, $parsingParams = false, $curBlock='', &$pointer = null) + { + if ($to === null) { + $to = strlen($in); + } + $first = substr($in, $from, 1); + + if ($first === false) { + throw new Dwoo_Compilation_Exception($this, 'Unexpected EOF, a template tag was not closed'); + } + + while ($first===" " || $first==="\n" || $first==="\t" || $first==="\r") { + if ($curBlock === 'root' && substr($in, $from, strlen($this->rd)) === $this->rd) { + // end template tag + $pointer += strlen($this->rd); + if ($this->debug) echo 'TEMPLATE PARSING ENDED
}', highlight_string(($output), true)); + array_shift($lines); + foreach ($lines as $i=>$line) { + echo ($i+1).'. '.$line."\r\n"; + } + } + if ($this->debug) echo '
+ * {* Create a simple link out of an url variable and add a special class attribute: *}
+ *
+ * {a $url class="external" /}
+ *
+ * {* Mark a link as active depending on some other variable : *}
+ *
+ * {a $link.url class=tif($link.active "active"); $link.title /}
+ *
+ * {* This is similar to: {$link.title} *}
+ *
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the use of this software.
+ *
+ * @author Jordi Boggiano
+ * {capture "foo"}
+ * Anything in here won't show, it will be saved for later use..
+ * {/capture}
+ * Output was : {$.capture.foo}
+ *
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the use of this software.
+ *
+ * @author Jordi Boggiano
+ * {foreach $array val}
+ * {$val.something}
+ * {/foreach}
+ *
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the use of this software.
+ *
+ * @author Jordi Boggiano
+ * {foreach $variable value}
+ * {$value.foo} {$value.bar}
+ * {/foreach}
+ *
+ *
+ * you can do :
+ *
+ *
+ * {loop $variable}
+ * {$foo} {$bar}
+ * {/loop}
+ *
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the use of this software.
+ *
+ * @author Jordi Boggiano
+ * {if $long.boring.prefix}
+ * {$long.boring.prefix.val} - {$long.boring.prefix.secondVal} - {$long.boring.prefix.thirdVal}
+ * {/if}
+ *
+ *
+ * you can use :
+ *
+ *
+ * {with $long.boring.prefix}
+ * {$val} - {$secondVal} - {$thirdVal}
+ * {/with}
+ *
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the use of this software.
+ *
+ * @author Jordi Boggiano
+ * {$c=2}
+ * {math "(a+b)*$c/4" a=3 b=5}
+ *
+ * output is : 4 ( = (3+5)*2/4)
+ *
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the use of this software.
+ *
+ * @author Jordi Boggiano + * * value : the string to process + *+ * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the use of this software. + * + * @author Jordi Boggiano
+ * {"a b c d
+ *
+ * e"|whitespace}
+ *
+ * results in : a b c d e
+ *
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the use of this software.
+ *
+ * @author Jordi Boggiano
+ * {array(a, b, c)} results in array(0=>'a', 1=>'b', 2=>'c')
+ * {array(a=foo, b=5, c=array(4,5))} results in array('a'=>'foo', 'b'=>5, 'c'=>array(0=>4, 1=>5))
+ *
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the use of this software.
+ *
+ * @author Jordi Boggiano '; +//var_dump($this->info['bitrate']); +//var_dump($this->info['audio']['bitrate']); +//var_dump($this->info['video']['bitrate']); +//echo ''; + if (isset($this->info['bitrate']) && empty($this->info['audio']['bitrate']) && empty($this->info['video']['bitrate'])) { + if (isset($this->info['audio']['dataformat']) && empty($this->info['video']['resolution_x'])) { + // audio only + $this->info['audio']['bitrate'] = $this->info['bitrate']; + } elseif (isset($this->info['video']['resolution_x']) && empty($this->info['audio']['dataformat'])) { + // video only + $this->info['video']['bitrate'] = $this->info['bitrate']; + } + } + + // Set playtime string + if (!empty($this->info['playtime_seconds']) && empty($this->info['playtime_string'])) { + $this->info['playtime_string'] = getid3_lib::PlaytimeString($this->info['playtime_seconds']); + } + } + + + function CalculateCompressionRatioVideo() { + if (empty($this->info['video'])) { + return false; + } + if (empty($this->info['video']['resolution_x']) || empty($this->info['video']['resolution_y'])) { + return false; + } + if (empty($this->info['video']['bits_per_sample'])) { + return false; + } + + switch ($this->info['video']['dataformat']) { + case 'bmp': + case 'gif': + case 'jpeg': + case 'jpg': + case 'png': + case 'tiff': + $FrameRate = 1; + $PlaytimeSeconds = 1; + $BitrateCompressed = $this->info['filesize'] * 8; + break; + + default: + if (!empty($this->info['video']['frame_rate'])) { + $FrameRate = $this->info['video']['frame_rate']; + } else { + return false; + } + if (!empty($this->info['playtime_seconds'])) { + $PlaytimeSeconds = $this->info['playtime_seconds']; + } else { + return false; + } + if (!empty($this->info['video']['bitrate'])) { + $BitrateCompressed = $this->info['video']['bitrate']; + } else { + return false; + } + break; + } + $BitrateUncompressed = $this->info['video']['resolution_x'] * $this->info['video']['resolution_y'] * $this->info['video']['bits_per_sample'] * $FrameRate; + + $this->info['video']['compression_ratio'] = $BitrateCompressed / $BitrateUncompressed; + return true; + } + + + function CalculateCompressionRatioAudio() { + if (empty($this->info['audio']['bitrate']) || empty($this->info['audio']['channels']) || empty($this->info['audio']['sample_rate'])) { + return false; + } + $this->info['audio']['compression_ratio'] = $this->info['audio']['bitrate'] / ($this->info['audio']['channels'] * $this->info['audio']['sample_rate'] * (!empty($this->info['audio']['bits_per_sample']) ? $this->info['audio']['bits_per_sample'] : 16)); + + if (!empty($this->info['audio']['streams'])) { + foreach ($this->info['audio']['streams'] as $streamnumber => $streamdata) { + if (!empty($streamdata['bitrate']) && !empty($streamdata['channels']) && !empty($streamdata['sample_rate'])) { + $this->info['audio']['streams'][$streamnumber]['compression_ratio'] = $streamdata['bitrate'] / ($streamdata['channels'] * $streamdata['sample_rate'] * (!empty($streamdata['bits_per_sample']) ? $streamdata['bits_per_sample'] : 16)); + } + } + } + return true; + } + + + function CalculateReplayGain() { + if (isset($this->info['replay_gain'])) { + $this->info['replay_gain']['reference_volume'] = 89; + if (isset($this->info['replay_gain']['track']['adjustment'])) { + $this->info['replay_gain']['track']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['track']['adjustment']; + } + if (isset($this->info['replay_gain']['album']['adjustment'])) { + $this->info['replay_gain']['album']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['album']['adjustment']; + } + + if (isset($this->info['replay_gain']['track']['peak'])) { + $this->info['replay_gain']['track']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['track']['peak']); + } + if (isset($this->info['replay_gain']['album']['peak'])) { + $this->info['replay_gain']['album']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['album']['peak']); + } + } + return true; + } + + function ProcessAudioStreams() { + if (!empty($this->info['audio']['bitrate']) || !empty($this->info['audio']['channels']) || !empty($this->info['audio']['sample_rate'])) { + if (!isset($this->info['audio']['streams'])) { + foreach ($this->info['audio'] as $key => $value) { + if ($key != 'streams') { + $this->info['audio']['streams'][0][$key] = $value; + } + } + } + } + return true; + } + + function getid3_tempnam() { + return tempnam($this->tempdir, 'gI3'); + } + +} + +?> \ No newline at end of file diff --git a/lib/getid3/module.audio.mp3.php b/lib/getid3/module.audio.mp3.php new file mode 100644 index 0000000..3dec8f6 --- /dev/null +++ b/lib/getid3/module.audio.mp3.php @@ -0,0 +1,2022 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio.mp3.php // +// module for analyzing MP3 files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +// number of frames to scan to determine if MPEG-audio sequence is valid +// Lower this number to 5-20 for faster scanning +// Increase this number to 50+ for most accurate detection of valid VBR/CBR +// mpeg-audio streams +define('GETID3_MP3_VALID_CHECK_FRAMES', 35); + + +class getid3_mp3 +{ + + var $allow_bruteforce = false; // forces getID3() to scan the file byte-by-byte and log all the valid audio frame headers - extremely slow, unrecommended, but may provide data from otherwise-unusuable files + + function getid3_mp3(&$fd, &$ThisFileInfo) { + + if (!$this->getOnlyMPEGaudioInfo($fd, $ThisFileInfo, $ThisFileInfo['avdataoffset'])) { + if ($this->allow_bruteforce) { + $ThisFileInfo['error'][] = 'Rescanning file in BruteForce mode'; + $this->getOnlyMPEGaudioInfoBruteForce($fd, $ThisFileInfo); + } + } + + + if (isset($ThisFileInfo['mpeg']['audio']['bitrate_mode'])) { + $ThisFileInfo['audio']['bitrate_mode'] = strtolower($ThisFileInfo['mpeg']['audio']['bitrate_mode']); + } + + if (((isset($ThisFileInfo['id3v2']['headerlength']) && ($ThisFileInfo['avdataoffset'] > $ThisFileInfo['id3v2']['headerlength'])) || (!isset($ThisFileInfo['id3v2']) && ($ThisFileInfo['avdataoffset'] > 0)))) { + + $synchoffsetwarning = 'Unknown data before synch '; + if (isset($ThisFileInfo['id3v2']['headerlength'])) { + $synchoffsetwarning .= '(ID3v2 header ends at '.$ThisFileInfo['id3v2']['headerlength'].', then '.($ThisFileInfo['avdataoffset'] - $ThisFileInfo['id3v2']['headerlength']).' bytes garbage, '; + } else { + $synchoffsetwarning .= '(should be at beginning of file, '; + } + $synchoffsetwarning .= 'synch detected at '.$ThisFileInfo['avdataoffset'].')'; + if (@$ThisFileInfo['audio']['bitrate_mode'] == 'cbr') { + + if (!empty($ThisFileInfo['id3v2']['headerlength']) && (($ThisFileInfo['avdataoffset'] - $ThisFileInfo['id3v2']['headerlength']) == $ThisFileInfo['mpeg']['audio']['framelength'])) { + + $synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90-3.92) DLL in CBR mode.'; + $ThisFileInfo['audio']['codec'] = 'LAME'; + $CurrentDataLAMEversionString = 'LAME3.'; + + } elseif (empty($ThisFileInfo['id3v2']['headerlength']) && ($ThisFileInfo['avdataoffset'] == $ThisFileInfo['mpeg']['audio']['framelength'])) { + + $synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90 - 3.92) DLL in CBR mode.'; + $ThisFileInfo['audio']['codec'] = 'LAME'; + $CurrentDataLAMEversionString = 'LAME3.'; + + } + + } + $ThisFileInfo['warning'][] = $synchoffsetwarning; + + } + + if (isset($ThisFileInfo['mpeg']['audio']['LAME'])) { + $ThisFileInfo['audio']['codec'] = 'LAME'; + if (!empty($ThisFileInfo['mpeg']['audio']['LAME']['long_version'])) { + $ThisFileInfo['audio']['encoder'] = rtrim($ThisFileInfo['mpeg']['audio']['LAME']['long_version'], "\x00"); + } elseif (!empty($ThisFileInfo['mpeg']['audio']['LAME']['short_version'])) { + $ThisFileInfo['audio']['encoder'] = rtrim($ThisFileInfo['mpeg']['audio']['LAME']['short_version'], "\x00"); + } + } + + $CurrentDataLAMEversionString = (!empty($CurrentDataLAMEversionString) ? $CurrentDataLAMEversionString : @$ThisFileInfo['audio']['encoder']); + if (!empty($CurrentDataLAMEversionString) && (substr($CurrentDataLAMEversionString, 0, 6) == 'LAME3.') && !preg_match('[0-9\)]', substr($CurrentDataLAMEversionString, -1))) { + // a version number of LAME that does not end with a number like "LAME3.92" + // or with a closing parenthesis like "LAME3.88 (alpha)" + // or a version of LAME with the LAMEtag-not-filled-in-DLL-mode bug (3.90-3.92) + + // not sure what the actual last frame length will be, but will be less than or equal to 1441 + $PossiblyLongerLAMEversion_FrameLength = 1441; + + // Not sure what version of LAME this is - look in padding of last frame for longer version string + $PossibleLAMEversionStringOffset = $ThisFileInfo['avdataend'] - $PossiblyLongerLAMEversion_FrameLength; + fseek($fd, $PossibleLAMEversionStringOffset); + $PossiblyLongerLAMEversion_Data = fread($fd, $PossiblyLongerLAMEversion_FrameLength); + switch (substr($CurrentDataLAMEversionString, -1)) { + case 'a': + case 'b': + // "LAME3.94a" will have a longer version string of "LAME3.94 (alpha)" for example + // need to trim off "a" to match longer string + $CurrentDataLAMEversionString = substr($CurrentDataLAMEversionString, 0, -1); + break; + } + if (($PossiblyLongerLAMEversion_String = strstr($PossiblyLongerLAMEversion_Data, $CurrentDataLAMEversionString)) !== false) { + if (substr($PossiblyLongerLAMEversion_String, 0, strlen($CurrentDataLAMEversionString)) == $CurrentDataLAMEversionString) { + $PossiblyLongerLAMEversion_NewString = substr($PossiblyLongerLAMEversion_String, 0, strspn($PossiblyLongerLAMEversion_String, 'LAME0123456789., (abcdefghijklmnopqrstuvwxyzJFSOND)')); //"LAME3.90.3" "LAME3.87 (beta 1, Sep 27 2000)" "LAME3.88 (beta)" + if (strlen($PossiblyLongerLAMEversion_NewString) > strlen(@$ThisFileInfo['audio']['encoder'])) { + $ThisFileInfo['audio']['encoder'] = $PossiblyLongerLAMEversion_NewString; + } + } + } + } + if (!empty($ThisFileInfo['audio']['encoder'])) { + $ThisFileInfo['audio']['encoder'] = rtrim($ThisFileInfo['audio']['encoder'], "\x00 "); + } + + switch (@$ThisFileInfo['mpeg']['audio']['layer']) { + case 1: + case 2: + $ThisFileInfo['audio']['dataformat'] = 'mp'.$ThisFileInfo['mpeg']['audio']['layer']; + break; + } + if (@$ThisFileInfo['fileformat'] == 'mp3') { + switch ($ThisFileInfo['audio']['dataformat']) { + case 'mp1': + case 'mp2': + case 'mp3': + $ThisFileInfo['fileformat'] = $ThisFileInfo['audio']['dataformat']; + break; + + default: + $ThisFileInfo['warning'][] = 'Expecting [audio][dataformat] to be mp1/mp2/mp3 when fileformat == mp3, [audio][dataformat] actually "'.$ThisFileInfo['audio']['dataformat'].'"'; + break; + } + } + + if (empty($ThisFileInfo['fileformat'])) { + unset($ThisFileInfo['fileformat']); + unset($ThisFileInfo['audio']['bitrate_mode']); + unset($ThisFileInfo['avdataoffset']); + unset($ThisFileInfo['avdataend']); + return false; + } + + $ThisFileInfo['mime_type'] = 'audio/mpeg'; + $ThisFileInfo['audio']['lossless'] = false; + + // Calculate playtime + if (!isset($ThisFileInfo['playtime_seconds']) && isset($ThisFileInfo['audio']['bitrate']) && ($ThisFileInfo['audio']['bitrate'] > 0)) { + $ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['audio']['bitrate']; + } + + $ThisFileInfo['audio']['encoder_options'] = $this->GuessEncoderOptions($ThisFileInfo); + + return true; + } + + + function GuessEncoderOptions(&$ThisFileInfo) { + // shortcuts + if (!empty($ThisFileInfo['mpeg']['audio'])) { + $thisfile_mpeg_audio = &$ThisFileInfo['mpeg']['audio']; + if (!empty($thisfile_mpeg_audio['LAME'])) { + $thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME']; + } + } + + $encoder_options = ''; + static $NamedPresetBitrates = array(16, 24, 40, 56, 112, 128, 160, 192, 256); + + if ((@$thisfile_mpeg_audio['VBR_method'] == 'Fraunhofer') && !empty($thisfile_mpeg_audio['VBR_quality'])) { + + $encoder_options = 'VBR q'.$thisfile_mpeg_audio['VBR_quality']; + + } elseif (!empty($thisfile_mpeg_audio_lame['preset_used']) && (!in_array($thisfile_mpeg_audio_lame['preset_used_id'], $NamedPresetBitrates))) { + + $encoder_options = $thisfile_mpeg_audio_lame['preset_used']; + + } elseif (!empty($thisfile_mpeg_audio_lame['vbr_quality'])) { + + static $KnownEncoderValues = array(); + if (empty($KnownEncoderValues)) { + + //$KnownEncoderValues[abrbitrate_minbitrate][vbr_quality][raw_vbr_method][raw_noise_shaping][raw_stereo_mode][ath_type][lowpass_frequency] = 'preset name'; + $KnownEncoderValues[0xFF][58][1][1][3][2][20500] = '--alt-preset insane'; // 3.90, 3.90.1, 3.92 + $KnownEncoderValues[0xFF][58][1][1][3][2][20600] = '--alt-preset insane'; // 3.90.2, 3.90.3, 3.91 + $KnownEncoderValues[0xFF][57][1][1][3][4][20500] = '--alt-preset insane'; // 3.94, 3.95 + $KnownEncoderValues['**'][78][3][2][3][2][19500] = '--alt-preset extreme'; // 3.90, 3.90.1, 3.92 + $KnownEncoderValues['**'][78][3][2][3][2][19600] = '--alt-preset extreme'; // 3.90.2, 3.91 + $KnownEncoderValues['**'][78][3][1][3][2][19600] = '--alt-preset extreme'; // 3.90.3 + $KnownEncoderValues['**'][78][4][2][3][2][19500] = '--alt-preset fast extreme'; // 3.90, 3.90.1, 3.92 + $KnownEncoderValues['**'][78][4][2][3][2][19600] = '--alt-preset fast extreme'; // 3.90.2, 3.90.3, 3.91 + $KnownEncoderValues['**'][78][3][2][3][4][19000] = '--alt-preset standard'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 + $KnownEncoderValues['**'][78][3][1][3][4][19000] = '--alt-preset standard'; // 3.90.3 + $KnownEncoderValues['**'][78][4][2][3][4][19000] = '--alt-preset fast standard'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 + $KnownEncoderValues['**'][78][4][1][3][4][19000] = '--alt-preset fast standard'; // 3.90.3 + $KnownEncoderValues['**'][88][4][1][3][3][19500] = '--r3mix'; // 3.90, 3.90.1, 3.92 + $KnownEncoderValues['**'][88][4][1][3][3][19600] = '--r3mix'; // 3.90.2, 3.90.3, 3.91 + $KnownEncoderValues['**'][67][4][1][3][4][18000] = '--r3mix'; // 3.94, 3.95 + $KnownEncoderValues['**'][68][3][2][3][4][18000] = '--alt-preset medium'; // 3.90.3 + $KnownEncoderValues['**'][68][4][2][3][4][18000] = '--alt-preset fast medium'; // 3.90.3 + + $KnownEncoderValues[0xFF][99][1][1][1][2][0] = '--preset studio'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 + $KnownEncoderValues[0xFF][58][2][1][3][2][20600] = '--preset studio'; // 3.90.3, 3.93.1 + $KnownEncoderValues[0xFF][58][2][1][3][2][20500] = '--preset studio'; // 3.93 + $KnownEncoderValues[0xFF][57][2][1][3][4][20500] = '--preset studio'; // 3.94, 3.95 + $KnownEncoderValues[0xC0][88][1][1][1][2][0] = '--preset cd'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 + $KnownEncoderValues[0xC0][58][2][2][3][2][19600] = '--preset cd'; // 3.90.3, 3.93.1 + $KnownEncoderValues[0xC0][58][2][2][3][2][19500] = '--preset cd'; // 3.93 + $KnownEncoderValues[0xC0][57][2][1][3][4][19500] = '--preset cd'; // 3.94, 3.95 + $KnownEncoderValues[0xA0][78][1][1][3][2][18000] = '--preset hifi'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 + $KnownEncoderValues[0xA0][58][2][2][3][2][18000] = '--preset hifi'; // 3.90.3, 3.93, 3.93.1 + $KnownEncoderValues[0xA0][57][2][1][3][4][18000] = '--preset hifi'; // 3.94, 3.95 + $KnownEncoderValues[0x80][67][1][1][3][2][18000] = '--preset tape'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 + $KnownEncoderValues[0x80][67][1][1][3][2][15000] = '--preset radio'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 + $KnownEncoderValues[0x70][67][1][1][3][2][15000] = '--preset fm'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 + $KnownEncoderValues[0x70][58][2][2][3][2][16000] = '--preset tape/radio/fm'; // 3.90.3, 3.93, 3.93.1 + $KnownEncoderValues[0x70][57][2][1][3][4][16000] = '--preset tape/radio/fm'; // 3.94, 3.95 + $KnownEncoderValues[0x38][58][2][2][0][2][10000] = '--preset voice'; // 3.90.3, 3.93, 3.93.1 + $KnownEncoderValues[0x38][57][2][1][0][4][15000] = '--preset voice'; // 3.94, 3.95 + $KnownEncoderValues[0x38][57][2][1][0][4][16000] = '--preset voice'; // 3.94a14 + $KnownEncoderValues[0x28][65][1][1][0][2][7500] = '--preset mw-us'; // 3.90, 3.90.1, 3.92 + $KnownEncoderValues[0x28][65][1][1][0][2][7600] = '--preset mw-us'; // 3.90.2, 3.91 + $KnownEncoderValues[0x28][58][2][2][0][2][7000] = '--preset mw-us'; // 3.90.3, 3.93, 3.93.1 + $KnownEncoderValues[0x28][57][2][1][0][4][10500] = '--preset mw-us'; // 3.94, 3.95 + $KnownEncoderValues[0x28][57][2][1][0][4][11200] = '--preset mw-us'; // 3.94a14 + $KnownEncoderValues[0x28][57][2][1][0][4][8800] = '--preset mw-us'; // 3.94a15 + $KnownEncoderValues[0x18][58][2][2][0][2][4000] = '--preset phon+/lw/mw-eu/sw'; // 3.90.3, 3.93.1 + $KnownEncoderValues[0x18][58][2][2][0][2][3900] = '--preset phon+/lw/mw-eu/sw'; // 3.93 + $KnownEncoderValues[0x18][57][2][1][0][4][5900] = '--preset phon+/lw/mw-eu/sw'; // 3.94, 3.95 + $KnownEncoderValues[0x18][57][2][1][0][4][6200] = '--preset phon+/lw/mw-eu/sw'; // 3.94a14 + $KnownEncoderValues[0x18][57][2][1][0][4][3200] = '--preset phon+/lw/mw-eu/sw'; // 3.94a15 + $KnownEncoderValues[0x10][58][2][2][0][2][3800] = '--preset phone'; // 3.90.3, 3.93.1 + $KnownEncoderValues[0x10][58][2][2][0][2][3700] = '--preset phone'; // 3.93 + $KnownEncoderValues[0x10][57][2][1][0][4][5600] = '--preset phone'; // 3.94, 3.95 + } + + if (isset($KnownEncoderValues[$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']])) { + + $encoder_options = $KnownEncoderValues[$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']]; + + } elseif (isset($KnownEncoderValues['**'][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']])) { + + $encoder_options = $KnownEncoderValues['**'][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']]; + + } elseif ($ThisFileInfo['audio']['bitrate_mode'] == 'vbr') { + + // http://gabriel.mp3-tech.org/mp3infotag.html + // int Quality = (100 - 10 * gfp->VBR_q - gfp->quality)h + + + $LAME_V_value = 10 - ceil($thisfile_mpeg_audio_lame['vbr_quality'] / 10); + $LAME_q_value = 100 - $thisfile_mpeg_audio_lame['vbr_quality'] - ($LAME_V_value * 10); + $encoder_options = '-V'.$LAME_V_value.' -q'.$LAME_q_value; + + } elseif ($ThisFileInfo['audio']['bitrate_mode'] == 'cbr') { + + $encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']).ceil($ThisFileInfo['audio']['bitrate'] / 1000); + + } else { + + $encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']); + + } + + } elseif (!empty($thisfile_mpeg_audio_lame['bitrate_abr'])) { + + $encoder_options = 'ABR'.$thisfile_mpeg_audio_lame['bitrate_abr']; + + } elseif (!empty($ThisFileInfo['audio']['bitrate'])) { + + if ($ThisFileInfo['audio']['bitrate_mode'] == 'cbr') { + $encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']).ceil($ThisFileInfo['audio']['bitrate'] / 1000); + } else { + $encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']); + } + + } + if (!empty($thisfile_mpeg_audio_lame['bitrate_min'])) { + $encoder_options .= ' -b'.$thisfile_mpeg_audio_lame['bitrate_min']; + } + + if (@$thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev'] || @$thisfile_mpeg_audio_lame['encoding_flags']['nogap_next']) { + $encoder_options .= ' --nogap'; + } + + if (!empty($thisfile_mpeg_audio_lame['lowpass_frequency'])) { + $ExplodedOptions = explode(' ', $encoder_options, 4); + if ($ExplodedOptions[0] == '--r3mix') { + $ExplodedOptions[1] = 'r3mix'; + } + switch ($ExplodedOptions[0]) { + case '--preset': + case '--alt-preset': + case '--r3mix': + if ($ExplodedOptions[1] == 'fast') { + $ExplodedOptions[1] .= ' '.$ExplodedOptions[2]; + } + switch ($ExplodedOptions[1]) { + case 'portable': + case 'medium': + case 'standard': + case 'extreme': + case 'insane': + case 'fast portable': + case 'fast medium': + case 'fast standard': + case 'fast extreme': + case 'fast insane': + case 'r3mix': + static $ExpectedLowpass = array( + 'insane|20500' => 20500, + 'insane|20600' => 20600, // 3.90.2, 3.90.3, 3.91 + 'medium|18000' => 18000, + 'fast medium|18000' => 18000, + 'extreme|19500' => 19500, // 3.90, 3.90.1, 3.92, 3.95 + 'extreme|19600' => 19600, // 3.90.2, 3.90.3, 3.91, 3.93.1 + 'fast extreme|19500' => 19500, // 3.90, 3.90.1, 3.92, 3.95 + 'fast extreme|19600' => 19600, // 3.90.2, 3.90.3, 3.91, 3.93.1 + 'standard|19000' => 19000, + 'fast standard|19000' => 19000, + 'r3mix|19500' => 19500, // 3.90, 3.90.1, 3.92 + 'r3mix|19600' => 19600, // 3.90.2, 3.90.3, 3.91 + 'r3mix|18000' => 18000, // 3.94, 3.95 + ); + if (!isset($ExpectedLowpass[$ExplodedOptions[1].'|'.$thisfile_mpeg_audio_lame['lowpass_frequency']]) && ($thisfile_mpeg_audio_lame['lowpass_frequency'] < 22050) && (round($thisfile_mpeg_audio_lame['lowpass_frequency'] / 1000) < round($thisfile_mpeg_audio['sample_rate'] / 2000))) { + $encoder_options .= ' --lowpass '.$thisfile_mpeg_audio_lame['lowpass_frequency']; + } + break; + + default: + break; + } + break; + } + } + + if (isset($thisfile_mpeg_audio_lame['raw']['source_sample_freq'])) { + if (($thisfile_mpeg_audio['sample_rate'] == 44100) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 1)) { + $encoder_options .= ' --resample 44100'; + } elseif (($thisfile_mpeg_audio['sample_rate'] == 48000) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 2)) { + $encoder_options .= ' --resample 48000'; + } elseif ($thisfile_mpeg_audio['sample_rate'] < 44100) { + switch ($thisfile_mpeg_audio_lame['raw']['source_sample_freq']) { + case 0: // <= 32000 + // may or may not be same as source frequency - ignore + break; + case 1: // 44100 + case 2: // 48000 + case 3: // 48000+ + $ExplodedOptions = explode(' ', $encoder_options, 4); + switch ($ExplodedOptions[0]) { + case '--preset': + case '--alt-preset': + switch ($ExplodedOptions[1]) { + case 'fast': + case 'portable': + case 'medium': + case 'standard': + case 'extreme': + case 'insane': + $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate']; + break; + + default: + static $ExpectedResampledRate = array( + 'phon+/lw/mw-eu/sw|16000' => 16000, + 'mw-us|24000' => 24000, // 3.95 + 'mw-us|32000' => 32000, // 3.93 + 'mw-us|16000' => 16000, // 3.92 + 'phone|16000' => 16000, + 'phone|11025' => 11025, // 3.94a15 + 'radio|32000' => 32000, // 3.94a15 + 'fm/radio|32000' => 32000, // 3.92 + 'fm|32000' => 32000, // 3.90 + 'voice|32000' => 32000); + if (!isset($ExpectedResampledRate[$ExplodedOptions[1].'|'.$thisfile_mpeg_audio['sample_rate']])) { + $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate']; + } + break; + } + break; + + case '--r3mix': + default: + $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate']; + break; + } + break; + } + } + } + if (empty($encoder_options) && !empty($ThisFileInfo['audio']['bitrate']) && !empty($ThisFileInfo['audio']['bitrate_mode'])) { + //$encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']).ceil($ThisFileInfo['audio']['bitrate'] / 1000); + $encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']); + } + + return $encoder_options; + } + + + function decodeMPEGaudioHeader($fd, $offset, &$ThisFileInfo, $recursivesearch=true, $ScanAsCBR=false, $FastMPEGheaderScan=false) { + + static $MPEGaudioVersionLookup; + static $MPEGaudioLayerLookup; + static $MPEGaudioBitrateLookup; + static $MPEGaudioFrequencyLookup; + static $MPEGaudioChannelModeLookup; + static $MPEGaudioModeExtensionLookup; + static $MPEGaudioEmphasisLookup; + if (empty($MPEGaudioVersionLookup)) { + $MPEGaudioVersionLookup = getid3_mp3::MPEGaudioVersionArray(); + $MPEGaudioLayerLookup = getid3_mp3::MPEGaudioLayerArray(); + $MPEGaudioBitrateLookup = getid3_mp3::MPEGaudioBitrateArray(); + $MPEGaudioFrequencyLookup = getid3_mp3::MPEGaudioFrequencyArray(); + $MPEGaudioChannelModeLookup = getid3_mp3::MPEGaudioChannelModeArray(); + $MPEGaudioModeExtensionLookup = getid3_mp3::MPEGaudioModeExtensionArray(); + $MPEGaudioEmphasisLookup = getid3_mp3::MPEGaudioEmphasisArray(); + } + + if ($offset >= $ThisFileInfo['avdataend']) { + $ThisFileInfo['error'][] = 'end of file encounter looking for MPEG synch'; + return false; + } + fseek($fd, $offset, SEEK_SET); + //$headerstring = fread($fd, 1441); // worst-case max length = 32kHz @ 320kbps layer 3 = 1441 bytes/frame + $headerstring = fread($fd, 226); // LAME header at offset 36 + 190 bytes of Xing/LAME data + + // MP3 audio frame structure: + // $aa $aa $aa $aa [$bb $bb] $cc... + // where $aa..$aa is the four-byte mpeg-audio header (below) + // $bb $bb is the optional 2-byte CRC + // and $cc... is the audio data + + $head4 = substr($headerstring, 0, 4); + + static $MPEGaudioHeaderDecodeCache = array(); + if (isset($MPEGaudioHeaderDecodeCache[$head4])) { + $MPEGheaderRawArray = $MPEGaudioHeaderDecodeCache[$head4]; + } else { + $MPEGheaderRawArray = getid3_mp3::MPEGaudioHeaderDecode($head4); + $MPEGaudioHeaderDecodeCache[$head4] = $MPEGheaderRawArray; + } + + static $MPEGaudioHeaderValidCache = array(); + + // Not in cache + if (!isset($MPEGaudioHeaderValidCache[$head4])) { + //$MPEGaudioHeaderValidCache[$head4] = getid3_mp3::MPEGaudioHeaderValid($MPEGheaderRawArray, false, true); // allow badly-formatted freeformat (from LAME 3.90 - 3.93.1) + $MPEGaudioHeaderValidCache[$head4] = getid3_mp3::MPEGaudioHeaderValid($MPEGheaderRawArray, false, false); + } + + // shortcut + if (!isset($ThisFileInfo['mpeg']['audio'])) { + $ThisFileInfo['mpeg']['audio'] = array(); + } + $thisfile_mpeg_audio = &$ThisFileInfo['mpeg']['audio']; + + + if ($MPEGaudioHeaderValidCache[$head4]) { + $thisfile_mpeg_audio['raw'] = $MPEGheaderRawArray; + } else { + $ThisFileInfo['error'][] = 'Invalid MPEG audio header at offset '.$offset; + return false; + } + + if (!$FastMPEGheaderScan) { + + $thisfile_mpeg_audio['version'] = $MPEGaudioVersionLookup[$thisfile_mpeg_audio['raw']['version']]; + $thisfile_mpeg_audio['layer'] = $MPEGaudioLayerLookup[$thisfile_mpeg_audio['raw']['layer']]; + + $thisfile_mpeg_audio['channelmode'] = $MPEGaudioChannelModeLookup[$thisfile_mpeg_audio['raw']['channelmode']]; + $thisfile_mpeg_audio['channels'] = (($thisfile_mpeg_audio['channelmode'] == 'mono') ? 1 : 2); + $thisfile_mpeg_audio['sample_rate'] = $MPEGaudioFrequencyLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['raw']['sample_rate']]; + $thisfile_mpeg_audio['protection'] = !$thisfile_mpeg_audio['raw']['protection']; + $thisfile_mpeg_audio['private'] = (bool) $thisfile_mpeg_audio['raw']['private']; + $thisfile_mpeg_audio['modeextension'] = $MPEGaudioModeExtensionLookup[$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['modeextension']]; + $thisfile_mpeg_audio['copyright'] = (bool) $thisfile_mpeg_audio['raw']['copyright']; + $thisfile_mpeg_audio['original'] = (bool) $thisfile_mpeg_audio['raw']['original']; + $thisfile_mpeg_audio['emphasis'] = $MPEGaudioEmphasisLookup[$thisfile_mpeg_audio['raw']['emphasis']]; + + $ThisFileInfo['audio']['channels'] = $thisfile_mpeg_audio['channels']; + $ThisFileInfo['audio']['sample_rate'] = $thisfile_mpeg_audio['sample_rate']; + + if ($thisfile_mpeg_audio['protection']) { + $thisfile_mpeg_audio['crc'] = getid3_lib::BigEndian2Int(substr($headerstring, 4, 2)); + } + + } + + if ($thisfile_mpeg_audio['raw']['bitrate'] == 15) { + // http://www.hydrogenaudio.org/?act=ST&f=16&t=9682&st=0 + $ThisFileInfo['warning'][] = 'Invalid bitrate index (15), this is a known bug in free-format MP3s encoded by LAME v3.90 - 3.93.1'; + $thisfile_mpeg_audio['raw']['bitrate'] = 0; + } + $thisfile_mpeg_audio['padding'] = (bool) $thisfile_mpeg_audio['raw']['padding']; + $thisfile_mpeg_audio['bitrate'] = $MPEGaudioBitrateLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['bitrate']]; + + if (($thisfile_mpeg_audio['bitrate'] == 'free') && ($offset == $ThisFileInfo['avdataoffset'])) { + // only skip multiple frame check if free-format bitstream found at beginning of file + // otherwise is quite possibly simply corrupted data + $recursivesearch = false; + } + + // For Layer 2 there are some combinations of bitrate and mode which are not allowed. + if (!$FastMPEGheaderScan && ($thisfile_mpeg_audio['layer'] == '2')) { + + $ThisFileInfo['audio']['dataformat'] = 'mp2'; + switch ($thisfile_mpeg_audio['channelmode']) { + + case 'mono': + if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] <= 192000)) { + // these are ok + } else { + $ThisFileInfo['error'][] = $thisfile_mpeg_audio['bitrate'].'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.'; + return false; + } + break; + + case 'stereo': + case 'joint stereo': + case 'dual channel': + if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] == 64000) || ($thisfile_mpeg_audio['bitrate'] >= 96000)) { + // these are ok + } else { + $ThisFileInfo['error'][] = intval(round($thisfile_mpeg_audio['bitrate'] / 1000)).'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.'; + return false; + } + break; + + } + + } + + + if ($ThisFileInfo['audio']['sample_rate'] > 0) { + $thisfile_mpeg_audio['framelength'] = getid3_mp3::MPEGaudioFrameLength($thisfile_mpeg_audio['bitrate'], $thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['layer'], (int) $thisfile_mpeg_audio['padding'], $ThisFileInfo['audio']['sample_rate']); + } + + $nextframetestoffset = $offset + 1; + if ($thisfile_mpeg_audio['bitrate'] != 'free') { + + $ThisFileInfo['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate']; + + if (isset($thisfile_mpeg_audio['framelength'])) { + $nextframetestoffset = $offset + $thisfile_mpeg_audio['framelength']; + } else { + $ThisFileInfo['error'][] = 'Frame at offset('.$offset.') is has an invalid frame length.'; + return false; + } + + } + + $ExpectedNumberOfAudioBytes = 0; + + //////////////////////////////////////////////////////////////////////////////////// + // Variable-bitrate headers + + if (substr($headerstring, 4 + 32, 4) == 'VBRI') { + // Fraunhofer VBR header is hardcoded 'VBRI' at offset 0x24 (36) + // specs taken from http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html + + $thisfile_mpeg_audio['bitrate_mode'] = 'vbr'; + $thisfile_mpeg_audio['VBR_method'] = 'Fraunhofer'; + $ThisFileInfo['audio']['codec'] = 'Fraunhofer'; + + $SideInfoData = substr($headerstring, 4 + 2, 32); + + $FraunhoferVBROffset = 36; + + $thisfile_mpeg_audio['VBR_encoder_version'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 4, 2)); // VbriVersion + $thisfile_mpeg_audio['VBR_encoder_delay'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 6, 2)); // VbriDelay + $thisfile_mpeg_audio['VBR_quality'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 8, 2)); // VbriQuality + $thisfile_mpeg_audio['VBR_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 10, 4)); // VbriStreamBytes + $thisfile_mpeg_audio['VBR_frames'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 14, 4)); // VbriStreamFrames + $thisfile_mpeg_audio['VBR_seek_offsets'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 18, 2)); // VbriTableSize + $thisfile_mpeg_audio['VBR_seek_scale'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 20, 2)); // VbriTableScale + $thisfile_mpeg_audio['VBR_entry_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 22, 2)); // VbriEntryBytes + $thisfile_mpeg_audio['VBR_entry_frames'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 24, 2)); // VbriEntryFrames + + $ExpectedNumberOfAudioBytes = $thisfile_mpeg_audio['VBR_bytes']; + + $previousbyteoffset = $offset; + for ($i = 0; $i < $thisfile_mpeg_audio['VBR_seek_offsets']; $i++) { + $Fraunhofer_OffsetN = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset, $thisfile_mpeg_audio['VBR_entry_bytes'])); + $FraunhoferVBROffset += $thisfile_mpeg_audio['VBR_entry_bytes']; + $thisfile_mpeg_audio['VBR_offsets_relative'][$i] = ($Fraunhofer_OffsetN * $thisfile_mpeg_audio['VBR_seek_scale']); + $thisfile_mpeg_audio['VBR_offsets_absolute'][$i] = ($Fraunhofer_OffsetN * $thisfile_mpeg_audio['VBR_seek_scale']) + $previousbyteoffset; + $previousbyteoffset += $Fraunhofer_OffsetN; + } + + + } else { + + // Xing VBR header is hardcoded 'Xing' at a offset 0x0D (13), 0x15 (21) or 0x24 (36) + // depending on MPEG layer and number of channels + + $VBRidOffset = getid3_mp3::XingVBRidOffset($thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['channelmode']); + $SideInfoData = substr($headerstring, 4 + 2, $VBRidOffset - 4); + + if ((substr($headerstring, $VBRidOffset, strlen('Xing')) == 'Xing') || (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Info')) { + // 'Xing' is traditional Xing VBR frame + // 'Info' is LAME-encoded CBR (This was done to avoid CBR files to be recognized as traditional Xing VBR files by some decoders.) + // 'Info' *can* legally be used to specify a VBR file as well, however. + + // http://www.multiweb.cz/twoinches/MP3inside.htm + //00..03 = "Xing" or "Info" + //04..07 = Flags: + // 0x01 Frames Flag set if value for number of frames in file is stored + // 0x02 Bytes Flag set if value for filesize in bytes is stored + // 0x04 TOC Flag set if values for TOC are stored + // 0x08 VBR Scale Flag set if values for VBR scale is stored + //08..11 Frames: Number of frames in file (including the first Xing/Info one) + //12..15 Bytes: File length in Bytes + //16..115 TOC (Table of Contents): + // Contains of 100 indexes (one Byte length) for easier lookup in file. Approximately solves problem with moving inside file. + // Each Byte has a value according this formula: + // (TOC[i] / 256) * fileLenInBytes + // So if song lasts eg. 240 sec. and you want to jump to 60. sec. (and file is 5 000 000 Bytes length) you can use: + // TOC[(60/240)*100] = TOC[25] + // and corresponding Byte in file is then approximately at: + // (TOC[25]/256) * 5000000 + //116..119 VBR Scale + + + // should be safe to leave this at 'vbr' and let it be overriden to 'cbr' if a CBR preset/mode is used by LAME +// if (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Xing') { + $thisfile_mpeg_audio['bitrate_mode'] = 'vbr'; + $thisfile_mpeg_audio['VBR_method'] = 'Xing'; +// } else { +// $ScanAsCBR = true; +// $thisfile_mpeg_audio['bitrate_mode'] = 'cbr'; +// } + + $thisfile_mpeg_audio['xing_flags_raw'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 4, 4)); + + $thisfile_mpeg_audio['xing_flags']['frames'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000001); + $thisfile_mpeg_audio['xing_flags']['bytes'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000002); + $thisfile_mpeg_audio['xing_flags']['toc'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000004); + $thisfile_mpeg_audio['xing_flags']['vbr_scale'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000008); + + if ($thisfile_mpeg_audio['xing_flags']['frames']) { + $thisfile_mpeg_audio['VBR_frames'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 8, 4)); + //$thisfile_mpeg_audio['VBR_frames']--; // don't count header Xing/Info frame + } + if ($thisfile_mpeg_audio['xing_flags']['bytes']) { + $thisfile_mpeg_audio['VBR_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 12, 4)); + } + + //if (($thisfile_mpeg_audio['bitrate'] == 'free') && !empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) { + if (!empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) { + + $framelengthfloat = $thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']; + + if ($thisfile_mpeg_audio['layer'] == '1') { + // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12 + //$ThisFileInfo['audio']['bitrate'] = ((($framelengthfloat / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12; + $ThisFileInfo['audio']['bitrate'] = ($framelengthfloat / 4) * $thisfile_mpeg_audio['sample_rate'] * (2 / $ThisFileInfo['audio']['channels']) / 12; + } else { + // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144 + //$ThisFileInfo['audio']['bitrate'] = (($framelengthfloat - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144; + $ThisFileInfo['audio']['bitrate'] = $framelengthfloat * $thisfile_mpeg_audio['sample_rate'] * (2 / $ThisFileInfo['audio']['channels']) / 144; + } + $thisfile_mpeg_audio['framelength'] = floor($framelengthfloat); + } + + if ($thisfile_mpeg_audio['xing_flags']['toc']) { + $LAMEtocData = substr($headerstring, $VBRidOffset + 16, 100); + for ($i = 0; $i < 100; $i++) { + $thisfile_mpeg_audio['toc'][$i] = ord($LAMEtocData{$i}); + } + } + if ($thisfile_mpeg_audio['xing_flags']['vbr_scale']) { + $thisfile_mpeg_audio['VBR_scale'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 116, 4)); + } + + + // http://gabriel.mp3-tech.org/mp3infotag.html + if (substr($headerstring, $VBRidOffset + 120, 4) == 'LAME') { + + // shortcut + $thisfile_mpeg_audio['LAME'] = array(); + $thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME']; + + + $thisfile_mpeg_audio_lame['long_version'] = substr($headerstring, $VBRidOffset + 120, 20); + $thisfile_mpeg_audio_lame['short_version'] = substr($thisfile_mpeg_audio_lame['long_version'], 0, 9); + + if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.90') { + + // extra 11 chars are not part of version string when LAMEtag present + unset($thisfile_mpeg_audio_lame['long_version']); + + // It the LAME tag was only introduced in LAME v3.90 + // http://www.hydrogenaudio.org/?act=ST&f=15&t=9933 + + // Offsets of various bytes in http://gabriel.mp3-tech.org/mp3infotag.html + // are assuming a 'Xing' identifier offset of 0x24, which is the case for + // MPEG-1 non-mono, but not for other combinations + $LAMEtagOffsetContant = $VBRidOffset - 0x24; + + // shortcuts + $thisfile_mpeg_audio_lame['RGAD'] = array('track'=>array(), 'album'=>array()); + $thisfile_mpeg_audio_lame_RGAD = &$thisfile_mpeg_audio_lame['RGAD']; + $thisfile_mpeg_audio_lame_RGAD_track = &$thisfile_mpeg_audio_lame_RGAD['track']; + $thisfile_mpeg_audio_lame_RGAD_album = &$thisfile_mpeg_audio_lame_RGAD['album']; + $thisfile_mpeg_audio_lame['raw'] = array(); + $thisfile_mpeg_audio_lame_raw = &$thisfile_mpeg_audio_lame['raw']; + + // byte $9B VBR Quality + // This field is there to indicate a quality level, although the scale was not precised in the original Xing specifications. + // Actually overwrites original Xing bytes + unset($thisfile_mpeg_audio['VBR_scale']); + $thisfile_mpeg_audio_lame['vbr_quality'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0x9B, 1)); + + // bytes $9C-$A4 Encoder short VersionString + $thisfile_mpeg_audio_lame['short_version'] = substr($headerstring, $LAMEtagOffsetContant + 0x9C, 9); + + // byte $A5 Info Tag revision + VBR method + $LAMEtagRevisionVBRmethod = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA5, 1)); + + $thisfile_mpeg_audio_lame['tag_revision'] = ($LAMEtagRevisionVBRmethod & 0xF0) >> 4; + $thisfile_mpeg_audio_lame_raw['vbr_method'] = $LAMEtagRevisionVBRmethod & 0x0F; + $thisfile_mpeg_audio_lame['vbr_method'] = getid3_mp3::LAMEvbrMethodLookup($thisfile_mpeg_audio_lame_raw['vbr_method']); + $thisfile_mpeg_audio['bitrate_mode'] = substr($thisfile_mpeg_audio_lame['vbr_method'], 0, 3); // usually either 'cbr' or 'vbr', but truncates 'vbr-old / vbr-rh' to 'vbr' + + // byte $A6 Lowpass filter value + $thisfile_mpeg_audio_lame['lowpass_frequency'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA6, 1)) * 100; + + // bytes $A7-$AE Replay Gain + // http://privatewww.essex.ac.uk/~djmrob/replaygain/rg_data_format.html + // bytes $A7-$AA : 32 bit floating point "Peak signal amplitude" + if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.94b') { + // LAME 3.94a16 and later - 9.23 fixed point + // ie 0x0059E2EE / (2^23) = 5890798 / 8388608 = 0.7022378444671630859375 + $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = (float) ((getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4))) / 8388608); + } else { + // LAME 3.94a15 and earlier - 32-bit floating point + // Actually 3.94a16 will fall in here too and be WRONG, but is hard to detect 3.94a16 vs 3.94a15 + $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = getid3_lib::LittleEndian2Float(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4)); + } + if ($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] == 0) { + unset($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']); + } else { + $thisfile_mpeg_audio_lame_RGAD['peak_db'] = getid3_lib::RGADamplitude2dB($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']); + } + + $thisfile_mpeg_audio_lame_raw['RGAD_track'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAB, 2)); + $thisfile_mpeg_audio_lame_raw['RGAD_album'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAD, 2)); + + + if ($thisfile_mpeg_audio_lame_raw['RGAD_track'] != 0) { + + $thisfile_mpeg_audio_lame_RGAD_track['raw']['name'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0xE000) >> 13; + $thisfile_mpeg_audio_lame_RGAD_track['raw']['originator'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x1C00) >> 10; + $thisfile_mpeg_audio_lame_RGAD_track['raw']['sign_bit'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x0200) >> 9; + $thisfile_mpeg_audio_lame_RGAD_track['raw']['gain_adjust'] = $thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x01FF; + $thisfile_mpeg_audio_lame_RGAD_track['name'] = getid3_lib::RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['name']); + $thisfile_mpeg_audio_lame_RGAD_track['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['originator']); + $thisfile_mpeg_audio_lame_RGAD_track['gain_db'] = getid3_lib::RGADadjustmentLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['gain_adjust'], $thisfile_mpeg_audio_lame_RGAD_track['raw']['sign_bit']); + + if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) { + $ThisFileInfo['replay_gain']['track']['peak'] = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude']; + } + $ThisFileInfo['replay_gain']['track']['originator'] = $thisfile_mpeg_audio_lame_RGAD_track['originator']; + $ThisFileInfo['replay_gain']['track']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_track['gain_db']; + } else { + unset($thisfile_mpeg_audio_lame_RGAD['track']); + } + if ($thisfile_mpeg_audio_lame_raw['RGAD_album'] != 0) { + + $thisfile_mpeg_audio_lame_RGAD_album['raw']['name'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0xE000) >> 13; + $thisfile_mpeg_audio_lame_RGAD_album['raw']['originator'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x1C00) >> 10; + $thisfile_mpeg_audio_lame_RGAD_album['raw']['sign_bit'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x0200) >> 9; + $thisfile_mpeg_audio_lame_RGAD_album['raw']['gain_adjust'] = $thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x01FF; + $thisfile_mpeg_audio_lame_RGAD_album['name'] = getid3_lib::RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['name']); + $thisfile_mpeg_audio_lame_RGAD_album['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['originator']); + $thisfile_mpeg_audio_lame_RGAD_album['gain_db'] = getid3_lib::RGADadjustmentLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['gain_adjust'], $thisfile_mpeg_audio_lame_RGAD_album['raw']['sign_bit']); + + if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) { + $ThisFileInfo['replay_gain']['album']['peak'] = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude']; + } + $ThisFileInfo['replay_gain']['album']['originator'] = $thisfile_mpeg_audio_lame_RGAD_album['originator']; + $ThisFileInfo['replay_gain']['album']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_album['gain_db']; + } else { + unset($thisfile_mpeg_audio_lame_RGAD['album']); + } + if (empty($thisfile_mpeg_audio_lame_RGAD)) { + unset($thisfile_mpeg_audio_lame['RGAD']); + } + + + // byte $AF Encoding flags + ATH Type + $EncodingFlagsATHtype = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAF, 1)); + $thisfile_mpeg_audio_lame['encoding_flags']['nspsytune'] = (bool) ($EncodingFlagsATHtype & 0x10); + $thisfile_mpeg_audio_lame['encoding_flags']['nssafejoint'] = (bool) ($EncodingFlagsATHtype & 0x20); + $thisfile_mpeg_audio_lame['encoding_flags']['nogap_next'] = (bool) ($EncodingFlagsATHtype & 0x40); + $thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev'] = (bool) ($EncodingFlagsATHtype & 0x80); + $thisfile_mpeg_audio_lame['ath_type'] = $EncodingFlagsATHtype & 0x0F; + + // byte $B0 if ABR {specified bitrate} else {minimal bitrate} + $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB0, 1)); + if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 2) { // Average BitRate (ABR) + $thisfile_mpeg_audio_lame['bitrate_abr'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']; + } elseif ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) { // Constant BitRate (CBR) + // ignore + } elseif ($thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] > 0) { // Variable BitRate (VBR) - minimum bitrate + $thisfile_mpeg_audio_lame['bitrate_min'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']; + } + + // bytes $B1-$B3 Encoder delays + $EncoderDelays = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB1, 3)); + $thisfile_mpeg_audio_lame['encoder_delay'] = ($EncoderDelays & 0xFFF000) >> 12; + $thisfile_mpeg_audio_lame['end_padding'] = $EncoderDelays & 0x000FFF; + + // byte $B4 Misc + $MiscByte = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB4, 1)); + $thisfile_mpeg_audio_lame_raw['noise_shaping'] = ($MiscByte & 0x03); + $thisfile_mpeg_audio_lame_raw['stereo_mode'] = ($MiscByte & 0x1C) >> 2; + $thisfile_mpeg_audio_lame_raw['not_optimal_quality'] = ($MiscByte & 0x20) >> 5; + $thisfile_mpeg_audio_lame_raw['source_sample_freq'] = ($MiscByte & 0xC0) >> 6; + $thisfile_mpeg_audio_lame['noise_shaping'] = $thisfile_mpeg_audio_lame_raw['noise_shaping']; + $thisfile_mpeg_audio_lame['stereo_mode'] = getid3_mp3::LAMEmiscStereoModeLookup($thisfile_mpeg_audio_lame_raw['stereo_mode']); + $thisfile_mpeg_audio_lame['not_optimal_quality'] = (bool) $thisfile_mpeg_audio_lame_raw['not_optimal_quality']; + $thisfile_mpeg_audio_lame['source_sample_freq'] = getid3_mp3::LAMEmiscSourceSampleFrequencyLookup($thisfile_mpeg_audio_lame_raw['source_sample_freq']); + + // byte $B5 MP3 Gain + $thisfile_mpeg_audio_lame_raw['mp3_gain'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB5, 1), false, true); + $thisfile_mpeg_audio_lame['mp3_gain_db'] = (getid3_lib::RGADamplitude2dB(2) / 4) * $thisfile_mpeg_audio_lame_raw['mp3_gain']; + $thisfile_mpeg_audio_lame['mp3_gain_factor'] = pow(2, ($thisfile_mpeg_audio_lame['mp3_gain_db'] / 6)); + + // bytes $B6-$B7 Preset and surround info + $PresetSurroundBytes = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB6, 2)); + // Reserved = ($PresetSurroundBytes & 0xC000); + $thisfile_mpeg_audio_lame_raw['surround_info'] = ($PresetSurroundBytes & 0x3800); + $thisfile_mpeg_audio_lame['surround_info'] = getid3_mp3::LAMEsurroundInfoLookup($thisfile_mpeg_audio_lame_raw['surround_info']); + $thisfile_mpeg_audio_lame['preset_used_id'] = ($PresetSurroundBytes & 0x07FF); + $thisfile_mpeg_audio_lame['preset_used'] = getid3_mp3::LAMEpresetUsedLookup($thisfile_mpeg_audio_lame); + if (!empty($thisfile_mpeg_audio_lame['preset_used_id']) && empty($thisfile_mpeg_audio_lame['preset_used'])) { + $ThisFileInfo['warning'][] = 'Unknown LAME preset used ('.$thisfile_mpeg_audio_lame['preset_used_id'].') - please report to info@getid3.org'; + } + if (($thisfile_mpeg_audio_lame['short_version'] == 'LAME3.90.') && !empty($thisfile_mpeg_audio_lame['preset_used_id'])) { + // this may change if 3.90.4 ever comes out + $thisfile_mpeg_audio_lame['short_version'] = 'LAME3.90.3'; + } + + // bytes $B8-$BB MusicLength + $thisfile_mpeg_audio_lame['audio_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB8, 4)); + $ExpectedNumberOfAudioBytes = (($thisfile_mpeg_audio_lame['audio_bytes'] > 0) ? $thisfile_mpeg_audio_lame['audio_bytes'] : $thisfile_mpeg_audio['VBR_bytes']); + + // bytes $BC-$BD MusicCRC + $thisfile_mpeg_audio_lame['music_crc'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBC, 2)); + + // bytes $BE-$BF CRC-16 of Info Tag + $thisfile_mpeg_audio_lame['lame_tag_crc'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBE, 2)); + + + // LAME CBR + if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) { + + $thisfile_mpeg_audio['bitrate_mode'] = 'cbr'; + $thisfile_mpeg_audio['bitrate'] = getid3_mp3::ClosestStandardMP3Bitrate($thisfile_mpeg_audio['bitrate']); + $ThisFileInfo['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate']; + //if (empty($thisfile_mpeg_audio['bitrate']) || (!empty($thisfile_mpeg_audio_lame['bitrate_min']) && ($thisfile_mpeg_audio_lame['bitrate_min'] != 255))) { + // $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio_lame['bitrate_min']; + //} + + } + + } + } + + } else { + + // not Fraunhofer or Xing VBR methods, most likely CBR (but could be VBR with no header) + $thisfile_mpeg_audio['bitrate_mode'] = 'cbr'; + if ($recursivesearch) { + $thisfile_mpeg_audio['bitrate_mode'] = 'vbr'; + if (getid3_mp3::RecursiveFrameScanning($fd, $ThisFileInfo, $offset, $nextframetestoffset, true)) { + $recursivesearch = false; + $thisfile_mpeg_audio['bitrate_mode'] = 'cbr'; + } + if ($thisfile_mpeg_audio['bitrate_mode'] == 'vbr') { + $ThisFileInfo['warning'][] = 'VBR file with no VBR header. Bitrate values calculated from actual frame bitrates.'; + } + } + + } + + } + + if (($ExpectedNumberOfAudioBytes > 0) && ($ExpectedNumberOfAudioBytes != ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']))) { + if ($ExpectedNumberOfAudioBytes > ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])) { + if (@$ThisFileInfo['fileformat'] == 'riff') { + // ignore, audio data is broken into chunks so will always be data "missing" + } elseif (($ExpectedNumberOfAudioBytes - ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])) == 1) { + $ThisFileInfo['warning'][] = 'Last byte of data truncated (this is a known bug in Meracl ID3 Tag Writer before v1.3.5)'; + } else { + $ThisFileInfo['warning'][] = 'Probable truncated file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, only found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' (short by '.($ExpectedNumberOfAudioBytes - ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])).' bytes)'; + } + } else { + if ((($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) - $ExpectedNumberOfAudioBytes) == 1) { + // $prenullbytefileoffset = ftell($fd); + // fseek($fd, $ThisFileInfo['avdataend'], SEEK_SET); + // $PossibleNullByte = fread($fd, 1); + // fseek($fd, $prenullbytefileoffset, SEEK_SET); + // if ($PossibleNullByte === "\x00") { + $ThisFileInfo['avdataend']--; + // $ThisFileInfo['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored'; + // } else { + // $ThisFileInfo['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' ('.(($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)'; + // } + } else { + $ThisFileInfo['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' ('.(($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)'; + } + } + } + + if (($thisfile_mpeg_audio['bitrate'] == 'free') && empty($ThisFileInfo['audio']['bitrate'])) { + if (($offset == $ThisFileInfo['avdataoffset']) && empty($thisfile_mpeg_audio['VBR_frames'])) { + $framebytelength = getid3_mp3::FreeFormatFrameLength($fd, $offset, $ThisFileInfo, true); + if ($framebytelength > 0) { + $thisfile_mpeg_audio['framelength'] = $framebytelength; + if ($thisfile_mpeg_audio['layer'] == '1') { + // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12 + $ThisFileInfo['audio']['bitrate'] = ((($framebytelength / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12; + } else { + // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144 + $ThisFileInfo['audio']['bitrate'] = (($framebytelength - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144; + } + } else { + $ThisFileInfo['error'][] = 'Error calculating frame length of free-format MP3 without Xing/LAME header'; + } + } + } + + if (@$thisfile_mpeg_audio['VBR_frames']) { + switch ($thisfile_mpeg_audio['bitrate_mode']) { + case 'vbr': + case 'abr': + if (($thisfile_mpeg_audio['version'] == '1') && ($thisfile_mpeg_audio['layer'] == 1)) { + $thisfile_mpeg_audio['VBR_bitrate'] = ((@$thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($ThisFileInfo['audio']['sample_rate'] / 384); + } elseif ((($thisfile_mpeg_audio['version'] == '2') || ($thisfile_mpeg_audio['version'] == '2.5')) && ($thisfile_mpeg_audio['layer'] == 3)) { + $thisfile_mpeg_audio['VBR_bitrate'] = ((@$thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($ThisFileInfo['audio']['sample_rate'] / 576); + } else { + $thisfile_mpeg_audio['VBR_bitrate'] = ((@$thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($ThisFileInfo['audio']['sample_rate'] / 1152); + } + if ($thisfile_mpeg_audio['VBR_bitrate'] > 0) { + $ThisFileInfo['audio']['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; + $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; // to avoid confusion + } + break; + } + } + + // End variable-bitrate headers + //////////////////////////////////////////////////////////////////////////////////// + + if ($recursivesearch) { + + if (!getid3_mp3::RecursiveFrameScanning($fd, $ThisFileInfo, $offset, $nextframetestoffset, $ScanAsCBR)) { + return false; + } + + } + + + //if (false) { + // // experimental side info parsing section - not returning anything useful yet + // + // $SideInfoBitstream = getid3_lib::BigEndian2Bin($SideInfoData); + // $SideInfoOffset = 0; + // + // if ($thisfile_mpeg_audio['version'] == '1') { + // if ($thisfile_mpeg_audio['channelmode'] == 'mono') { + // // MPEG-1 (mono) + // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9); + // $SideInfoOffset += 9; + // $SideInfoOffset += 5; + // } else { + // // MPEG-1 (stereo, joint-stereo, dual-channel) + // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9); + // $SideInfoOffset += 9; + // $SideInfoOffset += 3; + // } + // } else { // 2 or 2.5 + // if ($thisfile_mpeg_audio['channelmode'] == 'mono') { + // // MPEG-2, MPEG-2.5 (mono) + // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8); + // $SideInfoOffset += 8; + // $SideInfoOffset += 1; + // } else { + // // MPEG-2, MPEG-2.5 (stereo, joint-stereo, dual-channel) + // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8); + // $SideInfoOffset += 8; + // $SideInfoOffset += 2; + // } + // } + // + // if ($thisfile_mpeg_audio['version'] == '1') { + // for ($channel = 0; $channel < $ThisFileInfo['audio']['channels']; $channel++) { + // for ($scfsi_band = 0; $scfsi_band < 4; $scfsi_band++) { + // $thisfile_mpeg_audio['scfsi'][$channel][$scfsi_band] = substr($SideInfoBitstream, $SideInfoOffset, 1); + // $SideInfoOffset += 2; + // } + // } + // } + // for ($granule = 0; $granule < (($thisfile_mpeg_audio['version'] == '1') ? 2 : 1); $granule++) { + // for ($channel = 0; $channel < $ThisFileInfo['audio']['channels']; $channel++) { + // $thisfile_mpeg_audio['part2_3_length'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 12); + // $SideInfoOffset += 12; + // $thisfile_mpeg_audio['big_values'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9); + // $SideInfoOffset += 9; + // $thisfile_mpeg_audio['global_gain'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 8); + // $SideInfoOffset += 8; + // if ($thisfile_mpeg_audio['version'] == '1') { + // $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4); + // $SideInfoOffset += 4; + // } else { + // $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9); + // $SideInfoOffset += 9; + // } + // $thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); + // $SideInfoOffset += 1; + // + // if ($thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] == '1') { + // + // $thisfile_mpeg_audio['block_type'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 2); + // $SideInfoOffset += 2; + // $thisfile_mpeg_audio['mixed_block_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); + // $SideInfoOffset += 1; + // + // for ($region = 0; $region < 2; $region++) { + // $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5); + // $SideInfoOffset += 5; + // } + // $thisfile_mpeg_audio['table_select'][$granule][$channel][2] = 0; + // + // for ($window = 0; $window < 3; $window++) { + // $thisfile_mpeg_audio['subblock_gain'][$granule][$channel][$window] = substr($SideInfoBitstream, $SideInfoOffset, 3); + // $SideInfoOffset += 3; + // } + // + // } else { + // + // for ($region = 0; $region < 3; $region++) { + // $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5); + // $SideInfoOffset += 5; + // } + // + // $thisfile_mpeg_audio['region0_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4); + // $SideInfoOffset += 4; + // $thisfile_mpeg_audio['region1_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 3); + // $SideInfoOffset += 3; + // $thisfile_mpeg_audio['block_type'][$granule][$channel] = 0; + // } + // + // if ($thisfile_mpeg_audio['version'] == '1') { + // $thisfile_mpeg_audio['preflag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); + // $SideInfoOffset += 1; + // } + // $thisfile_mpeg_audio['scalefac_scale'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); + // $SideInfoOffset += 1; + // $thisfile_mpeg_audio['count1table_select'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); + // $SideInfoOffset += 1; + // } + // } + //} + + return true; + } + + function RecursiveFrameScanning(&$fd, &$ThisFileInfo, &$offset, &$nextframetestoffset, $ScanAsCBR) { + for ($i = 0; $i < GETID3_MP3_VALID_CHECK_FRAMES; $i++) { + // check next GETID3_MP3_VALID_CHECK_FRAMES frames for validity, to make sure we haven't run across a false synch + if (($nextframetestoffset + 4) >= $ThisFileInfo['avdataend']) { + // end of file + return true; + } + + $nextframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$ThisFileInfo['avdataend'], 'avdataoffset'=>$ThisFileInfo['avdataoffset']); + if (getid3_mp3::decodeMPEGaudioHeader($fd, $nextframetestoffset, $nextframetestarray, false)) { + if ($ScanAsCBR) { + // force CBR mode, used for trying to pick out invalid audio streams with + // valid(?) VBR headers, or VBR streams with no VBR header + if (!isset($nextframetestarray['mpeg']['audio']['bitrate']) || !isset($ThisFileInfo['mpeg']['audio']['bitrate']) || ($nextframetestarray['mpeg']['audio']['bitrate'] != $ThisFileInfo['mpeg']['audio']['bitrate'])) { + return false; + } + } + + + // next frame is OK, get ready to check the one after that + if (isset($nextframetestarray['mpeg']['audio']['framelength']) && ($nextframetestarray['mpeg']['audio']['framelength'] > 0)) { + $nextframetestoffset += $nextframetestarray['mpeg']['audio']['framelength']; + } else { + $ThisFileInfo['error'][] = 'Frame at offset ('.$offset.') is has an invalid frame length.'; + return false; + } + + } else { + + // next frame is not valid, note the error and fail, so scanning can contiue for a valid frame sequence + $ThisFileInfo['error'][] = 'Frame at offset ('.$offset.') is valid, but the next one at ('.$nextframetestoffset.') is not.'; + + return false; + } + } + return true; + } + + function FreeFormatFrameLength($fd, $offset, &$ThisFileInfo, $deepscan=false) { + fseek($fd, $offset, SEEK_SET); + $MPEGaudioData = fread($fd, 32768); + + $SyncPattern1 = substr($MPEGaudioData, 0, 4); + // may be different pattern due to padding + $SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) | 0x02).$SyncPattern1{3}; + if ($SyncPattern2 === $SyncPattern1) { + $SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) & 0xFD).$SyncPattern1{3}; + } + + $framelength = false; + $framelength1 = strpos($MPEGaudioData, $SyncPattern1, 4); + $framelength2 = strpos($MPEGaudioData, $SyncPattern2, 4); + if ($framelength1 > 4) { + $framelength = $framelength1; + } + if (($framelength2 > 4) && ($framelength2 < $framelength1)) { + $framelength = $framelength2; + } + if (!$framelength) { + + // LAME 3.88 has a different value for modeextension on the first frame vs the rest + $framelength1 = strpos($MPEGaudioData, substr($SyncPattern1, 0, 3), 4); + $framelength2 = strpos($MPEGaudioData, substr($SyncPattern2, 0, 3), 4); + + if ($framelength1 > 4) { + $framelength = $framelength1; + } + if (($framelength2 > 4) && ($framelength2 < $framelength1)) { + $framelength = $framelength2; + } + if (!$framelength) { + $ThisFileInfo['error'][] = 'Cannot find next free-format synch pattern ('.getid3_lib::PrintHexBytes($SyncPattern1).' or '.getid3_lib::PrintHexBytes($SyncPattern2).') after offset '.$offset; + return false; + } else { + $ThisFileInfo['warning'][] = 'ModeExtension varies between first frame and other frames (known free-format issue in LAME 3.88)'; + $ThisFileInfo['audio']['codec'] = 'LAME'; + $ThisFileInfo['audio']['encoder'] = 'LAME3.88'; + $SyncPattern1 = substr($SyncPattern1, 0, 3); + $SyncPattern2 = substr($SyncPattern2, 0, 3); + } + } + + if ($deepscan) { + + $ActualFrameLengthValues = array(); + $nextoffset = $offset + $framelength; + while ($nextoffset < ($ThisFileInfo['avdataend'] - 6)) { + fseek($fd, $nextoffset - 1, SEEK_SET); + $NextSyncPattern = fread($fd, 6); + if ((substr($NextSyncPattern, 1, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 1, strlen($SyncPattern2)) == $SyncPattern2)) { + // good - found where expected + $ActualFrameLengthValues[] = $framelength; + } elseif ((substr($NextSyncPattern, 0, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 0, strlen($SyncPattern2)) == $SyncPattern2)) { + // ok - found one byte earlier than expected (last frame wasn't padded, first frame was) + $ActualFrameLengthValues[] = ($framelength - 1); + $nextoffset--; + } elseif ((substr($NextSyncPattern, 2, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 2, strlen($SyncPattern2)) == $SyncPattern2)) { + // ok - found one byte later than expected (last frame was padded, first frame wasn't) + $ActualFrameLengthValues[] = ($framelength + 1); + $nextoffset++; + } else { + $ThisFileInfo['error'][] = 'Did not find expected free-format sync pattern at offset '.$nextoffset; + return false; + } + $nextoffset += $framelength; + } + if (count($ActualFrameLengthValues) > 0) { + $framelength = intval(round(array_sum($ActualFrameLengthValues) / count($ActualFrameLengthValues))); + } + } + return $framelength; + } + + function getOnlyMPEGaudioInfoBruteForce($fd, &$ThisFileInfo) { + + $MPEGaudioHeaderDecodeCache = array(); + $MPEGaudioHeaderValidCache = array(); + $MPEGaudioHeaderLengthCache = array(); + $MPEGaudioVersionLookup = getid3_mp3::MPEGaudioVersionArray(); + $MPEGaudioLayerLookup = getid3_mp3::MPEGaudioLayerArray(); + $MPEGaudioBitrateLookup = getid3_mp3::MPEGaudioBitrateArray(); + $MPEGaudioFrequencyLookup = getid3_mp3::MPEGaudioFrequencyArray(); + $MPEGaudioChannelModeLookup = getid3_mp3::MPEGaudioChannelModeArray(); + $MPEGaudioModeExtensionLookup = getid3_mp3::MPEGaudioModeExtensionArray(); + $MPEGaudioEmphasisLookup = getid3_mp3::MPEGaudioEmphasisArray(); + $LongMPEGversionLookup = array(); + $LongMPEGlayerLookup = array(); + $LongMPEGbitrateLookup = array(); + $LongMPEGpaddingLookup = array(); + $LongMPEGfrequencyLookup = array(); + + $Distribution['bitrate'] = array(); + $Distribution['frequency'] = array(); + $Distribution['layer'] = array(); + $Distribution['version'] = array(); + $Distribution['padding'] = array(); + + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + + $max_frames_scan = 5000; + $frames_scanned = 0; + + $previousvalidframe = $ThisFileInfo['avdataoffset']; + while (ftell($fd) < $ThisFileInfo['avdataend']) { + set_time_limit(30); + $head4 = fread($fd, 4); + if (strlen($head4) < 4) { + break; + } + if ($head4{0} != "\xFF") { + for ($i = 1; $i < 4; $i++) { + if ($head4{$i} == "\xFF") { + fseek($fd, $i - 4, SEEK_CUR); + continue 2; + } + } + continue; + } + if (!isset($MPEGaudioHeaderDecodeCache[$head4])) { + $MPEGaudioHeaderDecodeCache[$head4] = getid3_mp3::MPEGaudioHeaderDecode($head4); + } + if (!isset($MPEGaudioHeaderValidCache[$head4])) { + $MPEGaudioHeaderValidCache[$head4] = getid3_mp3::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$head4], false, false); + } + if ($MPEGaudioHeaderValidCache[$head4]) { + + if (!isset($MPEGaudioHeaderLengthCache[$head4])) { + $LongMPEGversionLookup[$head4] = $MPEGaudioVersionLookup[$MPEGaudioHeaderDecodeCache[$head4]['version']]; + $LongMPEGlayerLookup[$head4] = $MPEGaudioLayerLookup[$MPEGaudioHeaderDecodeCache[$head4]['layer']]; + $LongMPEGbitrateLookup[$head4] = $MPEGaudioBitrateLookup[$LongMPEGversionLookup[$head4]][$LongMPEGlayerLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['bitrate']]; + $LongMPEGpaddingLookup[$head4] = (bool) $MPEGaudioHeaderDecodeCache[$head4]['padding']; + $LongMPEGfrequencyLookup[$head4] = $MPEGaudioFrequencyLookup[$LongMPEGversionLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['sample_rate']]; + $MPEGaudioHeaderLengthCache[$head4] = getid3_mp3::MPEGaudioFrameLength( + $LongMPEGbitrateLookup[$head4], + $LongMPEGversionLookup[$head4], + $LongMPEGlayerLookup[$head4], + $LongMPEGpaddingLookup[$head4], + $LongMPEGfrequencyLookup[$head4]); + } + if ($MPEGaudioHeaderLengthCache[$head4] > 4) { + $WhereWeWere = ftell($fd); + fseek($fd, $MPEGaudioHeaderLengthCache[$head4] - 4, SEEK_CUR); + $next4 = fread($fd, 4); + if ($next4{0} == "\xFF") { + if (!isset($MPEGaudioHeaderDecodeCache[$next4])) { + $MPEGaudioHeaderDecodeCache[$next4] = getid3_mp3::MPEGaudioHeaderDecode($next4); + } + if (!isset($MPEGaudioHeaderValidCache[$next4])) { + $MPEGaudioHeaderValidCache[$next4] = getid3_mp3::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$next4], false, false); + } + if ($MPEGaudioHeaderValidCache[$next4]) { + fseek($fd, -4, SEEK_CUR); + + @$Distribution['bitrate'][$LongMPEGbitrateLookup[$head4]]++; + @$Distribution['layer'][$LongMPEGlayerLookup[$head4]]++; + @$Distribution['version'][$LongMPEGversionLookup[$head4]]++; + @$Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])]++; + @$Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]]++; + if ($max_frames_scan && (++$frames_scanned >= $max_frames_scan)) { + $pct_data_scanned = (ftell($fd) - $ThisFileInfo['avdataoffset']) / ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']); + $ThisFileInfo['warning'][] = 'too many MPEG audio frames to scan, only scanned first '.$max_frames_scan.' frames ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.'; + foreach ($Distribution as $key1 => $value1) { + foreach ($value1 as $key2 => $value2) { + $Distribution[$key1][$key2] = round($value2 / $pct_data_scanned); + } + } + break; + } + continue; + } + } + unset($next4); + fseek($fd, $WhereWeWere - 3, SEEK_SET); + } + + } + } + foreach ($Distribution as $key => $value) { + ksort($Distribution[$key], SORT_NUMERIC); + } + ksort($Distribution['version'], SORT_STRING); + $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = $Distribution['bitrate']; + $ThisFileInfo['mpeg']['audio']['frequency_distribution'] = $Distribution['frequency']; + $ThisFileInfo['mpeg']['audio']['layer_distribution'] = $Distribution['layer']; + $ThisFileInfo['mpeg']['audio']['version_distribution'] = $Distribution['version']; + $ThisFileInfo['mpeg']['audio']['padding_distribution'] = $Distribution['padding']; + if (count($Distribution['version']) > 1) { + $ThisFileInfo['error'][] = 'Corrupt file - more than one MPEG version detected'; + } + if (count($Distribution['layer']) > 1) { + $ThisFileInfo['error'][] = 'Corrupt file - more than one MPEG layer detected'; + } + if (count($Distribution['frequency']) > 1) { + $ThisFileInfo['error'][] = 'Corrupt file - more than one MPEG sample rate detected'; + } + + + $bittotal = 0; + foreach ($Distribution['bitrate'] as $bitratevalue => $bitratecount) { + if ($bitratevalue != 'free') { + $bittotal += ($bitratevalue * $bitratecount); + } + } + $ThisFileInfo['mpeg']['audio']['frame_count'] = array_sum($Distribution['bitrate']); + if ($ThisFileInfo['mpeg']['audio']['frame_count'] == 0) { + $ThisFileInfo['error'][] = 'no MPEG audio frames found'; + return false; + } + $ThisFileInfo['mpeg']['audio']['bitrate'] = ($bittotal / $ThisFileInfo['mpeg']['audio']['frame_count']); + $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = ((count($Distribution['bitrate']) > 0) ? 'vbr' : 'cbr'); + $ThisFileInfo['mpeg']['audio']['sample_rate'] = getid3_lib::array_max($Distribution['frequency'], true); + + $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['bitrate']; + $ThisFileInfo['audio']['bitrate_mode'] = $ThisFileInfo['mpeg']['audio']['bitrate_mode']; + $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate']; + $ThisFileInfo['audio']['dataformat'] = 'mp'.getid3_lib::array_max($Distribution['layer'], true); + $ThisFileInfo['fileformat'] = $ThisFileInfo['audio']['dataformat']; + + return true; + } + + + function getOnlyMPEGaudioInfo($fd, &$ThisFileInfo, $avdataoffset, $BitrateHistogram=false) { + + // looks for synch, decodes MPEG audio header + + static $MPEGaudioVersionLookup; + static $MPEGaudioLayerLookup; + static $MPEGaudioBitrateLookup; + if (empty($MPEGaudioVersionLookup)) { + $MPEGaudioVersionLookup = getid3_mp3::MPEGaudioVersionArray(); + $MPEGaudioLayerLookup = getid3_mp3::MPEGaudioLayerArray(); + $MPEGaudioBitrateLookup = getid3_mp3::MPEGaudioBitrateArray(); + + } + + fseek($fd, $avdataoffset, SEEK_SET); + $sync_seek_buffer_size = min(128 * 1024, $ThisFileInfo['avdataend'] - $avdataoffset); + if ($sync_seek_buffer_size <= 0) { + $ThisFileInfo['error'][] = 'Invalid $sync_seek_buffer_size at offset '.$avdataoffset; + return false; + } + $header = fread($fd, $sync_seek_buffer_size); + $sync_seek_buffer_size = strlen($header); + $SynchSeekOffset = 0; + while ($SynchSeekOffset < $sync_seek_buffer_size) { + + if ((($avdataoffset + $SynchSeekOffset) < $ThisFileInfo['avdataend']) && !feof($fd)) { + + if ($SynchSeekOffset > $sync_seek_buffer_size) { + // if a synch's not found within the first 128k bytes, then give up + $ThisFileInfo['error'][] = 'Could not find valid MPEG audio synch within the first '.round($sync_seek_buffer_size / 1024).'kB'; + if (isset($ThisFileInfo['audio']['bitrate'])) { + unset($ThisFileInfo['audio']['bitrate']); + } + if (isset($ThisFileInfo['mpeg']['audio'])) { + unset($ThisFileInfo['mpeg']['audio']); + } + if (empty($ThisFileInfo['mpeg'])) { + unset($ThisFileInfo['mpeg']); + } + return false; + + } elseif (feof($fd)) { + + $ThisFileInfo['error'][] = 'Could not find valid MPEG audio synch before end of file'; + if (isset($ThisFileInfo['audio']['bitrate'])) { + unset($ThisFileInfo['audio']['bitrate']); + } + if (isset($ThisFileInfo['mpeg']['audio'])) { + unset($ThisFileInfo['mpeg']['audio']); + } + if (isset($ThisFileInfo['mpeg']) && (!is_array($ThisFileInfo['mpeg']) || (count($ThisFileInfo['mpeg']) == 0))) { + unset($ThisFileInfo['mpeg']); + } + return false; + } + } + + if (($SynchSeekOffset + 1) >= strlen($header)) { + $ThisFileInfo['error'][] = 'Could not find valid MPEG synch before end of file'; + return false; + } + + if (($header{$SynchSeekOffset} == "\xFF") && ($header{($SynchSeekOffset + 1)} > "\xE0")) { // synch detected + + if (!isset($FirstFrameThisfileInfo) && !isset($ThisFileInfo['mpeg']['audio'])) { + $FirstFrameThisfileInfo = $ThisFileInfo; + $FirstFrameAVDataOffset = $avdataoffset + $SynchSeekOffset; + if (!getid3_mp3::decodeMPEGaudioHeader($fd, $avdataoffset + $SynchSeekOffset, $FirstFrameThisfileInfo, false)) { + // if this is the first valid MPEG-audio frame, save it in case it's a VBR header frame and there's + // garbage between this frame and a valid sequence of MPEG-audio frames, to be restored below + unset($FirstFrameThisfileInfo); + } + } + + $dummy = $ThisFileInfo; // only overwrite real data if valid header found + if (getid3_mp3::decodeMPEGaudioHeader($fd, $avdataoffset + $SynchSeekOffset, $dummy, true)) { + $ThisFileInfo = $dummy; + $ThisFileInfo['avdataoffset'] = $avdataoffset + $SynchSeekOffset; + switch (@$ThisFileInfo['fileformat']) { + case '': + case 'id3': + case 'ape': + case 'mp3': + $ThisFileInfo['fileformat'] = 'mp3'; + $ThisFileInfo['audio']['dataformat'] = 'mp3'; + break; + } + if (isset($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode']) && ($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr')) { + if (!(abs($ThisFileInfo['audio']['bitrate'] - $FirstFrameThisfileInfo['audio']['bitrate']) <= 1)) { + // If there is garbage data between a valid VBR header frame and a sequence + // of valid MPEG-audio frames the VBR data is no longer discarded. + $ThisFileInfo = $FirstFrameThisfileInfo; + $ThisFileInfo['avdataoffset'] = $FirstFrameAVDataOffset; + $ThisFileInfo['fileformat'] = 'mp3'; + $ThisFileInfo['audio']['dataformat'] = 'mp3'; + $dummy = $ThisFileInfo; + unset($dummy['mpeg']['audio']); + $GarbageOffsetStart = $FirstFrameAVDataOffset + $FirstFrameThisfileInfo['mpeg']['audio']['framelength']; + $GarbageOffsetEnd = $avdataoffset + $SynchSeekOffset; + if (getid3_mp3::decodeMPEGaudioHeader($fd, $GarbageOffsetEnd, $dummy, true, true)) { + + $ThisFileInfo = $dummy; + $ThisFileInfo['avdataoffset'] = $GarbageOffsetEnd; + $ThisFileInfo['warning'][] = 'apparently-valid VBR header not used because could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.'), but did find valid CBR stream starting at '.$GarbageOffsetEnd; + + } else { + + $ThisFileInfo['warning'][] = 'using data from VBR header even though could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.')'; + + } + } + } + if (isset($ThisFileInfo['mpeg']['audio']['bitrate_mode']) && ($ThisFileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr') && !isset($ThisFileInfo['mpeg']['audio']['VBR_method'])) { + // VBR file with no VBR header + $BitrateHistogram = true; + } + + if ($BitrateHistogram) { + + $ThisFileInfo['mpeg']['audio']['stereo_distribution'] = array('stereo'=>0, 'joint stereo'=>0, 'dual channel'=>0, 'mono'=>0); + $ThisFileInfo['mpeg']['audio']['version_distribution'] = array('1'=>0, '2'=>0, '2.5'=>0); + + if ($ThisFileInfo['mpeg']['audio']['version'] == '1') { + if ($ThisFileInfo['mpeg']['audio']['layer'] == 3) { + $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 40000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 320000=>0); + } elseif ($ThisFileInfo['mpeg']['audio']['layer'] == 2) { + $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 320000=>0, 384000=>0); + } elseif ($ThisFileInfo['mpeg']['audio']['layer'] == 1) { + $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 64000=>0, 96000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 288000=>0, 320000=>0, 352000=>0, 384000=>0, 416000=>0, 448000=>0); + } + } elseif ($ThisFileInfo['mpeg']['audio']['layer'] == 1) { + $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 144000=>0, 160000=>0, 176000=>0, 192000=>0, 224000=>0, 256000=>0); + } else { + $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 8000=>0, 16000=>0, 24000=>0, 32000=>0, 40000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 144000=>0, 160000=>0); + } + + $dummy = array('error'=>$ThisFileInfo['error'], 'warning'=>$ThisFileInfo['warning'], 'avdataend'=>$ThisFileInfo['avdataend'], 'avdataoffset'=>$ThisFileInfo['avdataoffset']); + $synchstartoffset = $ThisFileInfo['avdataoffset']; + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + + // you can play with these numbers: + $max_frames_scan = 50000; + $max_scan_segments = 10; + + // don't play with these numbers: + $FastMode = false; + $SynchErrorsFound = 0; + $frames_scanned = 0; + $this_scan_segment = 0; + $frames_scan_per_segment = ceil($max_frames_scan / $max_scan_segments); + $pct_data_scanned = 0; + for ($current_segment = 0; $current_segment < $max_scan_segments; $current_segment++) { +//echo 'was at '.ftell($fd).'
Status | Test | Message |
---|
\n \n';
+
+// tH[̊_1 (ʂ̃pbg ` Ŝ HSBARGB +- Ƃ)
+selfytag[1] = ' \n\n';
+
+// tH[̊_2 (Ŝ HSBARGB +- Ƃ ` Of[V)
+selfytag[2] = ' \n\nGradation';
+
+// tH[̊_3 (Of[V ` pbg̒ljE폜{^)
+selfytag[3] = ' \n\nPalette';
+
+// tH[̊_4 (pbg̒ljE폜{^ ` Z[u{^)
+selfytag[4] = '\nSave';
+
+// tH[̊_5 (Z[u{^ ` Default H++/+S-B ǂ炩)
+selfytag[5] = ' \n\nDefault';
+
+// tH[̊_6 (Default H++/+S-B ǂ炩 ` pbg̃Abv/_E[h)
+selfytag[6] = ' \n\nUpdata ';
+
+// tH[I
+selfytag[7] = ' \n | \n
"+B.stripScripts()+" |
/gi,"")},createEditField:function(){var B;if(this.options.loadTextURL){B=this.options.loadingText}else{B=this.getText()}var C=this;if(this.options.rows==1&&!this.hasHTMLLineBreaks(B)){this.options.textarea=false;var A=document.createElement("input");A.obj=this;A.type="text";A.name=this.options.paramName;A.value=B;A.style.backgroundColor=this.options.highlightcolor;A.className="editor_field";var E=this.options.size||this.options.cols||0;if(E!=0){A.size=E}if(this.options.submitOnBlur){A.onblur=this.onSubmit.bind(this)}this.editField=A}else{this.options.textarea=true;var D=document.createElement("textarea");D.obj=this;D.name=this.options.paramName;D.value=this.convertHTMLLineBreaks(B);D.rows=this.options.rows;D.cols=this.options.cols||40;D.className="editor_field";if(this.options.submitOnBlur){D.onblur=this.onSubmit.bind(this)}this.editField=D}if(this.options.loadTextURL){this.loadExternalText()}this.form.appendChild(this.editField)},getText:function(){return this.element.innerHTML},loadExternalText:function(){Element.addClassName(this.form,this.options.loadingClassName);this.editField.disabled=true;new Ajax.Request(this.options.loadTextURL,Object.extend({asynchronous:true,onComplete:this.onLoadedExternalText.bind(this)},this.options.ajaxOptions))},onLoadedExternalText:function(A){Element.removeClassName(this.form,this.options.loadingClassName);this.editField.disabled=false;this.editField.value=A.responseText.stripTags();Field.scrollFreeActivate(this.editField)},onclickCancel:function(){this.onComplete();this.leaveEditMode();return false},onFailure:function(A){this.options.onFailure(A);if(this.oldInnerHTML){this.element.innerHTML=this.oldInnerHTML;this.oldInnerHTML=null}return false},onSubmit:function(){var A=this.form;var B=this.editField.value;this.onLoading();if(this.options.evalScripts){new Ajax.Request(this.url,Object.extend({parameters:this.options.callback(A,B),onComplete:this.onComplete.bind(this),onFailure:this.onFailure.bind(this),asynchronous:true,evalScripts:true},this.options.ajaxOptions))}else{new Ajax.Updater({success:this.element,failure:null},this.url,Object.extend({parameters:this.options.callback(A,B),onComplete:this.onComplete.bind(this),onFailure:this.onFailure.bind(this)},this.options.ajaxOptions))}if(arguments.length>1){Event.stop(arguments[0])}return false},onLoading:function(){this.saving=true;this.removeForm();this.leaveHover();this.showSaving()},showSaving:function(){this.oldInnerHTML=this.element.innerHTML;this.element.innerHTML=this.options.savingText;Element.addClassName(this.element,this.options.savingClassName);this.element.style.backgroundColor=this.originalBackground;Element.show(this.element)},removeForm:function(){if(this.form){if(this.form.parentNode){Element.remove(this.form)}this.form=null}},enterHover:function(){if(this.saving){return }this.element.style.backgroundColor=this.options.highlightcolor;if(this.effect){this.effect.cancel()}Element.addClassName(this.element,this.options.hoverClassName)},leaveHover:function(){if(this.options.backgroundColor){this.element.style.backgroundColor=this.oldBackground}Element.removeClassName(this.element,this.options.hoverClassName);if(this.saving){return }this.effect=new Effect.Highlight(this.element,{startcolor:this.options.highlightcolor,endcolor:this.options.highlightendcolor,restorecolor:this.originalBackground})},leaveEditMode:function(){Element.removeClassName(this.element,this.options.savingClassName);this.removeForm();this.leaveHover();this.element.style.backgroundColor=this.originalBackground;Element.show(this.element);if(this.options.externalControl){Element.show(this.options.externalControl)}this.editing=false;this.saving=false;this.oldInnerHTML=null;this.onLeaveEditMode()},onComplete:function(A){this.leaveEditMode();this.options.onComplete.bind(this)(A,this.element)},onEnterEditMode:function(){},onLeaveEditMode:function(){},dispose:function(){if(this.oldInnerHTML){this.element.innerHTML=this.oldInnerHTML}this.leaveEditMode();Event.stopObserving(this.element,"click",this.onclickListener);Event.stopObserving(this.element,"mouseover",this.mouseoverListener);Event.stopObserving(this.element,"mouseout",this.mouseoutListener);if(this.options.externalControl){Event.stopObserving(this.options.externalControl,"click",this.onclickListener);Event.stopObserving(this.options.externalControl,"mouseover",this.mouseoverListener);Event.stopObserving(this.options.externalControl,"mouseout",this.mouseoutListener)}}};Ajax.InPlaceCollectionEditor=Class.create();Object.extend(Ajax.InPlaceCollectionEditor.prototype,Ajax.InPlaceEditor.prototype);Object.extend(Ajax.InPlaceCollectionEditor.prototype,{createEditField:function(){if(!this.cached_selectTag){var C=document.createElement("select");var B=this.options.collection||[];var A;B.each(function(E,D){A=document.createElement("option");A.value=(E instanceof Array)?E[0]:E;if((typeof this.options.value=="undefined")&&((E instanceof Array)?this.element.innerHTML==E[1]:E==A.value)){A.selected=true}if(this.options.value==A.value){A.selected=true}A.appendChild(document.createTextNode((E instanceof Array)?E[1]:E));C.appendChild(A)}.bind(this));this.cached_selectTag=C}this.editField=this.cached_selectTag;if(this.options.loadTextURL){this.loadExternalText()}this.form.appendChild(this.editField);this.options.callback=function(E,D){return"value="+encodeURIComponent(D)}}});Form.Element.DelayedObserver=Class.create();Form.Element.DelayedObserver.prototype={initialize:function(C,B,A){this.delay=B||0.5;this.element=$(C);this.callback=A;this.timer=null;this.lastValue=$F(this.element);Event.observe(this.element,"keyup",this.delayedListener.bindAsEventListener(this))},delayedListener:function(A){if(this.lastValue==$F(this.element)){return }if(this.timer){clearTimeout(this.timer)}this.timer=setTimeout(this.onTimerEvent.bind(this),this.delay*1000);this.lastValue=$F(this.element)},onTimerEvent:function(){this.timer=null;this.callback(this.element,$F(this.element))}};if(typeof Effect=="undefined"){}var Droppables={drops:[],remove:function(A){this.drops=this.drops.reject(function(B){return B.element==$(A)})},add:function(C){C=$(C);var B=Object.extend({greedy:true,hoverclass:null,tree:false},arguments[1]||{});if(B.containment){B._containers=[];var A=B.containment;if((typeof A=="object")&&(A.constructor==Array)){A.each(function(D){B._containers.push($(D))})}else{B._containers.push($(A))}}if(B.accept){B.accept=[B.accept].flatten()}Element.makePositioned(C);B.element=C;this.drops.push(B)},findDeepestChild:function(A){deepest=A[0];for(i=1;i
Status | Test | Message |
---|