');
129 | bcePageNameCell.parent('span').css('white-space', 'nowrap')
130 | .prepend('');
131 | });
132 |
133 | $('.bceTogglePageInfo').on('click', function () {
134 | $('.pageinfo-visible').not($(this).parents('.pageinfo-visible').first()).removeClass('pageinfo-visible');
135 | $(this).parents('tr').first().toggleClass('pageinfo-visible');
136 | })
137 | });
138 |
139 | //csv export
140 | //$('.Inputfield_iframe').hide();
141 | $(document).on('click', '.children_export_csv', function () {
142 | $('#download').attr('src', config.urls.admin +
143 | "setup/children-csv-export/?pid=" +
144 | $(this).attr('data-pageid') +
145 | "&pti=" + ($("#Inputfield_pagesToInclude").val() ? $("#Inputfield_pagesToInclude").val() : $("#Inputfield_pagesToInclude").val()) +
146 | "&fns=" + ($("#Inputfield_userExportFields").val() ? $("#Inputfield_userExportFields").val() : $("#Inputfield_exportFields").val()) +
147 | "&cs=" + $("#Inputfield_export_column_separator").val() +
148 | "&ce=" + $("#Inputfield_export_column_enclosure").val() +
149 | "&ext=" + $("#Inputfield_export_extension").val() +
150 | "&nfr=" + ($("#Inputfield_export_names_first_row").is(':checkbox') ? $("#Inputfield_export_names_first_row").attr('checked') : $("#Inputfield_export_names_first_row").val()) +
151 | "&mvs=" + $("#Inputfield_export_multiple_values_separator").val() +
152 | "&fe=" + ($("#Inputfield_format_export").is(':checkbox') ? $("#Inputfield_format_export").attr('checked') : $("#Inputfield_format_export").val())
153 | );
154 | return false;
155 | });
156 |
157 |
158 | /**
159 | * Add toggle controls to column headers (check/uncheck all items in a column)
160 | *
161 | * @author tpr
162 | * @updated 2015-09-15
163 | */
164 |
165 | var bce_adminDataTableSelector = '.batchChildTableContainer .AdminDataTable',
166 | bce_columnControlClass = 'bce-column-toggle',
167 | bce_allowedColumnControls = ['input.langActiveStatus', 'input.hiddenStatus', 'input.unpublishedStatus', 'i.InputfieldChildTableRowDeleteLink'],
168 | bce_toggleControl = '',
169 | bce_controlEventType = 'change',
170 | bce_deletedRowClass = 'InputfieldChildTableRowDeleted',
171 | bce_isColumnControlsAdded = false;
172 |
173 | $(document).one('mouseover', bce_adminDataTableSelector, function () {
174 | addBceColumnControls();
175 | });
176 |
177 | /**
178 | * Set column header checkbox state based on all items of the column.
179 | *
180 | * @param $obj jQuery object
181 | */
182 | function setColumnControlStates($obj) {
183 |
184 | var elem = $obj.is('input') ? 'input' : 'i',
185 | index = $obj.parent('td, th').index(),
186 | columnControl = $(bce_adminDataTableSelector + ' th:eq(' + index + ') input'),
187 | columnItems = $(bce_adminDataTableSelector).find('td:nth-child(' + parseInt(index + 1) + ')'),
188 | checkedItems = (elem == 'input') ? columnItems.find(':checked') : $(bce_adminDataTableSelector).find('.' + bce_deletedRowClass),
189 | allItems = columnItems.find(elem);
190 |
191 | if (checkedItems.length !== 0 && checkedItems.length === allItems.length) {
192 | columnControl.prop('checked', 1);
193 | } else {
194 | columnControl.prop('checked', 0);
195 | }
196 | }
197 |
198 | /**
199 | * Add control toggle checkboxes to BCE table.
200 | *
201 | * @returns {boolean}
202 | */
203 | function addBceColumnControls() {
204 |
205 | if (bce_isColumnControlsAdded) {
206 | return false;
207 | }
208 |
209 | if ($(bce_adminDataTableSelector).length === 0) {
210 | return false;
211 | }
212 |
213 | // do not add controls if there is no more than 1 row
214 | if ($(bce_adminDataTableSelector + ' tbody tr').length <= 1) {
215 | return false;
216 | }
217 |
218 | //$(bce_adminDataTableSelector + ' tbody').on('click', 'input[type="checkbox"], i.InputfieldChildTableRowDeleteLink', function () {
219 | $(bce_adminDataTableSelector + ' tbody').on('click', 'input[type="checkbox"]', function () {
220 | setColumnControlStates($(this));
221 | });
222 |
223 | // add new controls
224 | for (var i = 0; i < bce_allowedColumnControls.length; i++) {
225 |
226 | var currentControl = bce_allowedColumnControls[i];
227 |
228 | // skip non-existing elements
229 | if (!$(currentControl).length) {
230 | continue;
231 | }
232 |
233 | // get index of first checkbox in the first row
234 | var index = $(bce_adminDataTableSelector + ' ' + currentControl + ':eq(0)').parent().index();
235 |
236 | // do the add
237 | $(bce_adminDataTableSelector + ' th:eq(' + index + ')').prepend($(bce_toggleControl));
238 |
239 | // set initial checkbox states
240 | setColumnControlStates($(bce_adminDataTableSelector + ' th:eq(' + index + ') input'));
241 |
242 | // add event
243 | addColumnControlEvent(bce_adminDataTableSelector, currentControl, index);
244 | }
245 |
246 | // disable thead break to multiline
247 | $(bce_adminDataTableSelector + ' thead').css('white-space', 'nowrap');
248 |
249 | bce_isColumnControlsAdded = true;
250 |
251 | return true;
252 | }
253 |
254 |
255 | /**
256 | * Add event on column toggle checkboxes.
257 | *
258 | * @param bce_adminDataTableSelector
259 | * @param currentControl
260 | * @param index
261 | */
262 | function addColumnControlEvent(bce_adminDataTableSelector, currentControl, index) {
263 |
264 | var currentColumnControlSelector = bce_adminDataTableSelector + ' thead th:eq(' + index + ') .' + bce_columnControlClass;
265 |
266 | $(currentColumnControlSelector).on(bce_controlEventType, function () {
267 |
268 | var currentColumnControl = $(currentColumnControlSelector),
269 | toggleState = currentColumnControl.is(':checked');
270 |
271 | $(bce_adminDataTableSelector + ' tbody tr').each(function () {
272 |
273 | var currentRow = $(this),
274 | currentItem = currentRow.find('td:eq(' + index + ') ' + currentControl);
275 |
276 | // toggle checkboxes state or trigger clicks
277 | if (currentItem.is('input')) {
278 | currentItem.prop('checked', toggleState);
279 |
280 | } else if (currentItem.is('i')) {
281 | if (toggleState) {
282 | if (!currentRow.hasClass(bce_deletedRowClass)) {
283 | currentItem.trigger('bce-delete-row');
284 | }
285 | } else {
286 | if (currentRow.hasClass(bce_deletedRowClass)) {
287 | currentItem.trigger('bce-delete-row');
288 | }
289 | }
290 | }
291 | });
292 | });
293 | }
294 |
295 | // End of adding toggle controls to column headers.
296 |
297 |
298 | $(document).on('click', '.batchChildTableEdit', batchChildTableDialog);
299 |
300 | var i = 0;
301 | $(document).on('click', 'button.InputfieldChildTableAddRow', function () {
302 | var c = 1;
303 | i++;
304 | var $table = $(this).closest('.Inputfield').find('table.bceEditTable');
305 | var $tbody = $table.find('tbody:first');
306 | var numRows = $tbody.children('tr').length;
307 | var $row = $tbody.children(":first").clone(true);
308 | var $titleCell = $row.find("td:eq(" + (c++) + ")").find(':input');
309 | $titleCell.attr('placeholder', ''); // empty the title placeholder text
310 | $row.find("td:eq(" + (c++) + ")").html(''); //empty the name cell
311 |
312 | var colCount = 0;
313 | $row.find("td").each(function () {
314 | colCount++;
315 | });
316 | if (colCount == 10) {
317 | $row.find("td:eq(" + (c++) + ")").find(':checkbox').prop('checked', false); //uncheck active checkbox
318 | }
319 |
320 | $row.find("td:eq(" + (c++) + ")").html($('#defaultTemplates').html()); //set template data
321 | $row.find("td:eq(" + (c++) + ")").find(':checkbox').prop('checked', false); //uncheck hidden checkbox
322 | $row.find("td:eq(" + (c++) + ")").find(':checkbox').prop('checked', false); //uncheck unpublished checkbox
323 | $row.find("td:eq(" + (c++) + ")").html(''); //empty the view button cell
324 | $row.find("td:eq(" + (c++) + ")").html(''); //empty the edit button cell
325 | $row.find("td:eq(" + (c++) + ")").html(''); //empty the delete button cell
326 |
327 | //in case the first row was set for deletion - the new row, cloned from this, would also be set for deletion, so need to remove class and restore opacity
328 | $row.removeClass('InputfieldChildTableRowDeleted');
329 | $row.css('opacity', 1.0);
330 |
331 | $row.find(":input").each(function () {
332 | var $input = $(this);
333 | if ($($input).is("select")) {
334 | $input.attr("name", "templateId[new_" + i + "]");
335 | }
336 | else if ($($input).hasClass('langActiveStatus')) {
337 | $input.attr("name", "langActiveStatus[new_" + i + "]");
338 | }
339 | else if ($($input).hasClass('hiddenStatus')) {
340 | $input.attr("name", "hiddenStatus[new_" + i + "]");
341 | }
342 | else if ($($input).hasClass('unpublishedStatus')) {
343 | $input.attr("name", "unpublishedStatus[new_" + i + "]");
344 | }
345 | else if ($input.is('.InputfieldChildTableRowSort')) $input.val(numRows);
346 | else {
347 | $input.attr("name", "individualChildTitles[new_" + i + "]");
348 | $input.attr('value', '');
349 | $input.attr('id', '');
350 | }
351 | });
352 |
353 | $tbody.append($row);
354 | $table.show();
355 | $titleCell.focus();
356 | return false;
357 | });
358 |
359 | // make rows sortable - trigger this on first ("one") mouseover of a sort handle in case BCE fieldset is being opened via AJAX
360 | $(document).one('mouseover', '.InputfieldChildTableRowSortHandle', function () {
361 | $("table.AdminDataTable").each(function () {
362 | batchChildTableSortable($(this));
363 | });
364 | });
365 |
366 | // row deletion
367 | var deleteIds;
368 | $(document).on('click bce-delete-row', '.InputfieldChildTableRowDeleteLink', function () {
369 | var $row = $(this).closest('tr');
370 | var $input = $('.InputfieldChildTableRowDelete');
371 |
372 | if ($row.is('.InputfieldChildTableRowDeleted')) {
373 | // undelete
374 | $row.removeClass('InputfieldChildTableRowDeleted');
375 | $row.css('opacity', 1.0);
376 | deleteIds = $input.val().replace($row.find("td:eq(1)").find("input").attr("id") + ',', '');
377 | $input.val(deleteIds);
378 |
379 | } else {
380 | // delete
381 | $row.addClass('InputfieldChildTableRowDeleted');
382 | $row.css('opacity', 0.3);
383 | deleteIds = $input.val() + $row.find("td:eq(1)").find("input").attr("id") + ',';
384 | $input.val(deleteIds);
385 | }
386 |
387 | setColumnControlStates($(this));
388 | });
389 |
390 | //Add or remove "Title" label from Text/Paste CSV textarea if user changes ignore first row setting
391 | $(document).on('change', '#Inputfield_userIgnoreFirstRow', function () {
392 | var initialAddText = $('textarea[name=childPagesAdd]').val();
393 | var initialUpdateText = $('textarea[name=childPagesUpdate]').val();
394 | var initialReplaceText = $('textarea[name=childPagesReplace]').val();
395 | if ($(this).is(':checked')) {
396 | if ($('textarea[name=childPagesAdd]').length) $('textarea[name=childPagesAdd]').val("Title\n" + initialAddText);
397 | if ($('textarea[name=childPagesUpdate]').length) $('textarea[name=childPagesUpdate]').val("Title\n" + initialUpdateText);
398 | if ($('textarea[name=childPagesReplace]').length) $('textarea[name=childPagesReplace]').val("Title\n" + initialReplaceText);
399 | }
400 | else {
401 | if ($('textarea[name=childPagesAdd]').length) $('textarea[name=childPagesAdd]').val(removeFirstLine(initialAddText));
402 | if ($('textarea[name=childPagesUpdate]').length) $('textarea[name=childPagesUpdate]').val(removeFirstLine(initialUpdateText));
403 | if ($('textarea[name=childPagesReplace]').length) $('textarea[name=childPagesReplace]').val(removeFirstLine(initialReplaceText));
404 | }
405 | });
406 |
407 | });
408 |
409 | function removeFirstLine(text) {
410 | // break the textblock into an array of lines
411 | var lines = text.split('\n');
412 | // remove one line, starting at the first position
413 | lines.splice(0, 1);
414 | // join the array back into a single string
415 | return lines.join('\n');
416 | }
417 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 | {description}
294 | Copyright (C) {year} {fullname}
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | {signature of Ty Coon}, 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
--------------------------------------------------------------------------------
/ProcessChildrenCsvExport.module.php:
--------------------------------------------------------------------------------
1 | __('Process Children CSV Export'),
25 | 'version' => '1.8.31',
26 | 'summary' => __('Helper module for BatchChildEditor for creating CSV to export'),
27 | 'href' => 'http://modules.processwire.com/modules/batch-child-editor/',
28 | 'singular' => true,
29 | 'autoload' => false,
30 | 'permission' => 'batch-child-editor',
31 | 'requires' => 'BatchChildEditor',
32 | 'page' => array(
33 | 'name' => 'children-csv-export',
34 | 'parent' => 'setup',
35 | 'title' => 'Children CSV Export',
36 | 'status' => 'hidden'
37 | )
38 | );
39 | }
40 |
41 | /**
42 | * Name used for the page created in the admin
43 | *
44 | */
45 | const adminPageName = 'children-csv-export';
46 |
47 |
48 |
49 | /**
50 | * Initialize the module
51 | *
52 | */
53 | public function init() {
54 | parent::init();
55 | $this->addHook('Page::exportCsv', $this, 'exportCsv'); /* not limited to table-csv-export permission because only relevant to front-end */
56 | }
57 |
58 | /**
59 | * Executed when root url for module is accessed
60 | *
61 | */
62 | public function ___execute() {
63 | $this->exportCsv();
64 | }
65 |
66 |
67 | public function outputCSV($data, $delimiter, $enclosure) {
68 | $output = fopen("php://output", "w");
69 | foreach ($data as $row) {
70 | fputcsv($output, $row, $delimiter == "tab" ? chr(9) : $delimiter, $enclosure, '\\');
71 | }
72 | fclose($output);
73 | }
74 |
75 |
76 | public function exportCsv($event = NULL) {
77 |
78 | $systemFields = wire('modules')->get("BatchChildEditor")->systemFields;
79 |
80 | $configSettings = wire('modules')->getModuleConfigData("BatchChildEditor");
81 |
82 | $pp = !is_null($event) ? $event->object : wire('pages')->get((int) wire('input')->get->pid);
83 | $delimiter = !is_null($event) ? $event->arguments(0) : wire('input')->get->cs;
84 | $enclosure = !is_null($event) ? $event->arguments(1) : wire('input')->get->ce;
85 | $extension = !is_null($event) ? $event->arguments(2) : wire('input')->get->ext;
86 | $namesFirstRow = !is_null($event) ? $event->arguments(3) : wire('input')->get->nfr;
87 | $namesFirstRow = $namesFirstRow == 'checked' || $namesFirstRow == '1' ? true : false;
88 | $exportMultipleValuesSeparator = !is_null($event) ? $event->arguments(4) : wire('input')->get->mvs;
89 | $formatExport = !is_null($event) ? $event->arguments(5) : wire('input')->get->fe;
90 | $formatExport = $formatExport == 'checked' || $formatExport == 1 ? true : false;
91 | $pagesToInclude = !is_null($event) ? $event->arguments(6) : wire('input')->get->pti;
92 | $fieldNames = !is_null($event) ? $event->arguments(7) : explode(',', wire('input')->get->fns);
93 |
94 | //if settings not supplied, use defaults from page or module config settings
95 | $currentData = isset($configSettings['configurablePages']) && in_array($pp->id, $configSettings['configurablePages']) && isset($configSettings['pageSettings'][$pp->id]) && $configSettings['pageSettings'][$pp->id] ? $configSettings['pageSettings'][$pp->id] : $configSettings;
96 |
97 | if(isset($currentData['parentPage']) && $currentData['parentPage']) $pp = wire('pages')->get($currentData['parentPage']);
98 |
99 | $delimiter = $delimiter ? $delimiter : $currentData['csvExportFieldSeparator'];
100 | $enclosure = $enclosure ? $enclosure : $currentData['csvExportFieldEnclosure'];
101 | $extension = $extension ? $extension : $currentData['csvExportExtension'];
102 | $namesFirstRow = isset($namesFirstRow) ? $namesFirstRow : $currentData['columnsFirstRow'];
103 | $exportMultipleValuesSeparator = $exportMultipleValuesSeparator ? $exportMultipleValuesSeparator : $currentData['exportMultipleValuesSeparator'];
104 | if($exportMultipleValuesSeparator == '\r') $exportMultipleValuesSeparator = chr(13);
105 | if($exportMultipleValuesSeparator == '\n') $exportMultipleValuesSeparator = chr(10);
106 | $formatExport = isset($formatExport) ? $formatExport : $currentData['formatExport'];
107 | if(!$pagesToInclude) {
108 | $pagesToInclude = isset($currentData['pagesToInclude']) ? $currentData['pagesToInclude'] : '';
109 | }
110 | $fieldNames = $fieldNames ? $fieldNames : $currentData['exportFields'];
111 |
112 | if($fieldNames[0] == 'undefined') {
113 | $fieldNames = array();
114 | // if fields not defined, then get list from first child
115 | foreach ($pp->child()->fields as $f) $fieldNames[] = $f->name;
116 | }
117 |
118 | $csv = array();
119 | $i=0;
120 | $children = $pagesToInclude != '' && $pagesToInclude != 'undefined' ? $pp->children($pagesToInclude) : $pp->children();
121 | foreach($children as $p) {
122 |
123 | $p->of($formatExport); //needed to have fields formatted in the CSV
124 |
125 | //Names in First Row
126 | if($i==0 && $namesFirstRow == true) {
127 | foreach($fieldNames as $fieldName) {
128 |
129 | //exclude unsupported field types
130 | if(wire('fields')->$fieldName && (wire('fields')->$fieldName->type == 'FieldtypeTable' ||
131 | wire('fields')->$fieldName->type == 'FieldtypeRepeater' ||
132 | wire('fields')->$fieldName->type == 'FieldtypePageTable' ||
133 | wire('fields')->$fieldName->type == 'FieldsetOpen' ||
134 | wire('fields')->$fieldName->type == 'FieldsetClose' ||
135 | wire('fields')->$fieldName->type == 'FieldsetTabOpen' ||
136 | wire('fields')->$fieldName->type == 'FieldsetTabClose'
137 | )) continue;
138 |
139 | //FieldtypeTextareas
140 | if(wire('fields')->$fieldName && wire('fields')->$fieldName->type == 'FieldtypeTextareas') {
141 | $field = wire('fields')->get($fieldName);
142 | $subfields = $field->type->getBlankValue(new Page(), $field);
143 | foreach($subfields as $subFieldName => $value) {
144 | $csv[$i][] = $field->type->getLabel($field, $subFieldName) ? $field->type->getLabel($field, $subFieldName) : $subFieldName;
145 | }
146 | }
147 | /*
148 | //Repeaters
149 | elseif(wire('fields')->$fieldName && wire('fields')->$fieldName->type == 'FieldtypeRepeater') {
150 | $field = wire('fields')->get($fieldName);
151 | $subfields = $field->repeaterFields;
152 | foreach($subfields as $sf) {
153 | $subField = wire('fields')->get($sf);
154 | $csv[$i][] = $subField->label ? $subField->label : $subField->name;
155 | }
156 | }
157 | */
158 | //All other fieldtypes
159 | elseif(isset($fieldName) && $fieldName !== 'undefined') {
160 | if(array_key_exists($fieldName, $systemFields)) {
161 | $fieldLabel = $systemFields[$fieldName];
162 | }
163 | else {
164 | $label = wire('fields')->get($fieldName)->label;
165 | $fieldLabel = $label ? $label : $fieldName;
166 | }
167 | $csv[$i][] = $fieldLabel;
168 | }
169 | }
170 | }
171 |
172 | //All Data Rows
173 | $i++;
174 | foreach($fieldNames as $fieldName) {
175 |
176 | $formattedValue = '';
177 |
178 | if($fieldName == 'created_formatted' || $fieldName == 'modified_formatted' || $fieldName == 'published_formatted') {
179 | $dateFieldName = str_replace('_formatted', '', $fieldName);
180 | $formattedValue = date('Y-m-d H:i:s', $p->$dateFieldName);
181 | }
182 | //exclude unsupported field types
183 | elseif(wire('fields')->$fieldName && (wire('fields')->$fieldName->type == 'FieldtypeTable' ||
184 | wire('fields')->$fieldName->type == 'FieldtypeRepeater' ||
185 | wire('fields')->$fieldName->type == 'FieldtypePageTable' ||
186 | wire('fields')->$fieldName->type == 'FieldsetOpen' ||
187 | wire('fields')->$fieldName->type == 'FieldsetClose' ||
188 | wire('fields')->$fieldName->type == 'FieldsetTabOpen' ||
189 | wire('fields')->$fieldName->type == 'FieldsetTabClose'
190 | )) {
191 | continue;
192 | }
193 | elseif(!$p->$fieldName) {
194 | $formattedValue = '';
195 | }
196 | //FieldtypeTextareas
197 | elseif(wire('fields')->$fieldName && wire('fields')->$fieldName->type == 'FieldtypeTextareas') {
198 | $field = wire('fields')->get($fieldName);
199 | $subfields = $field->type->getBlankValue(new Page(), $field);
200 | foreach($subfields as $subFieldName => $value) {
201 | $csv[$i][] = $p->$fieldName->$subFieldName;
202 | }
203 | }
204 | /*
205 | //FieldtypeRepeaters
206 | elseif(wire('fields')->$fieldName && wire('fields')->$fieldName->type == 'FieldtypeRepeater') {
207 | foreach($p->$fieldName as $item) {
208 | foreach($item->fields as $subField) {
209 | $csv[$i][] = $item->$subField;
210 | }
211 | }
212 | }
213 | */
214 | //Page fields
215 | elseif(wire('fields')->$fieldName && wire('fields')->$fieldName->type instanceof FieldtypePage) {
216 | if(method_exists($p->$fieldName,'implode')) {
217 | if($p->$fieldName->implode($exportMultipleValuesSeparator, 'title')) { // title available
218 | $formattedValue = $p->$fieldName->implode($exportMultipleValuesSeparator, 'title');
219 | }
220 | else { // no title so use name - eg a page field selecting from the user template
221 | $formattedValue = $p->$fieldName->implode($exportMultipleValuesSeparator, 'name');
222 | }
223 | }
224 | else {
225 | $formattedValue = $p->$fieldName->title ? $p->$fieldName->title : $p->$fieldName->name;
226 | }
227 | }
228 | //FieldtypeMultiplier and FieldtypeFile
229 | elseif(wire('fields')->$fieldName && (wire('fields')->$fieldName->type == 'FieldtypeMultiplier' || wire('fields')->$fieldName->type == 'FieldtypeOptions' || wire('fields')->$fieldName->type instanceof FieldtypeFile)) {
230 | if(wire('fields')->$fieldName->type instanceof FieldtypeFile) $p->of(false); //formatting off required if output format is "Rendered string of text"
231 | if(count($p->$fieldName)>0) {
232 | $values = array();
233 | foreach($p->$fieldName as $value) {
234 | if(wire('fields')->$fieldName->type instanceof FieldtypeFile) {
235 | $values[] = $value->filename;
236 | }
237 | elseif($formatExport && wire('fields')->$fieldName->type == 'FieldtypeOptions') {
238 | $values[] = $value->title;
239 | }
240 | else {
241 | $values[] = $value;
242 | }
243 | }
244 | $formattedValue = implode($exportMultipleValuesSeparator, $values);
245 | }
246 | $p->of($formatExport);
247 | }
248 | elseif(wire('fields')->$fieldName && wire('fields')->$fieldName->type == "FieldtypeMapMarker") {
249 | foreach(array('address', 'lat', 'lng', 'zoom', 'status') as $subFieldName) {
250 | $values[] = $p->$fieldName->$subFieldName;
251 | }
252 | $formattedValue = implode($exportMultipleValuesSeparator, $values);
253 | }
254 | elseif(wire('fields')->$fieldName && wire('fields')->$fieldName->type instanceof FieldtypeMulti && count($p->$fieldName) === 0) {
255 | $formattedValue = '';
256 | }
257 | //All other fields
258 | else {
259 | $formattedValue = $p->$fieldName;
260 | }
261 |
262 | //Populate $csv array for all fields that don't have subfields and are therefore already populated, like FieldtypeTextareas
263 | if(array_key_exists($fieldName, $systemFields) || (wire('fields')->$fieldName && wire('fields')->$fieldName->type != 'FieldtypeTextareas')) {
264 | $csv[$i][] = $formattedValue;
265 | }
266 |
267 | }
268 |
269 | }
270 |
271 | header("Content-type: text/csv");
272 | header("Content-Disposition: attachment; filename=".$pp->name .".".$extension);
273 | header("Pragma: no-cache");
274 | header("Expires: 0");
275 |
276 | $this->outputCSV($csv, $delimiter, $enclosure);
277 | exit;
278 |
279 | }
280 |
281 | }
282 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | BatchChildEditor
2 | ================
3 |
4 | Processwire module for quick batch creation (titles only or CSV import for other fields), editing, sorting, deletion, and CSV export of all children under a given page.
5 |
6 | This module adds a variety of importing, editing, and exporting tools. The interface can be added to the Children Tab, or in a new dedicated tab, or placed inline with other fields in the Content tab.
7 |
8 | #### Modes
9 | 1. Lister - Embeds a customized Lister interface. Installation of ListerPro will allow inline ajax editing of displayed fields.
10 | 2. Edit - Allows you to quidkly rename existing child pages, add new child pages, sort, and delete pages. It also has a modal edit link from the page name to allow easy access to edit all the fields on the page.
11 | 3. Add - Adds newly entered page titles as child pages to the list of existing siblings. You could create a list of pages in Word or whatever and just paste them in here and viola!
12 | 4. Update - This allows updating of existing pages - the title, name and all other fields.
13 | 5. Replace - This completely replaces all existing child pages with new pages. There are checks that prevent this method working if there are any child pages with their own children or other content fields that are not empty. This check can be disabled in the module config settings, but please be very careful with this.
14 | 6. Export to CSV - Generates a CSV file containing the fields for all child pages. Fields to be exported can be fixed or customized by the user. Also includes an API export method.
15 |
16 | In Add, Update, and Replace modes you can enter CSV formatted rows to populate all text/numeric fields. This can be used to create new pages or to update existing pages. CSV field pairings can be defined to make it easy for editors to periodically create new pages, or update the fields in existing pages.
17 |
18 | There is also an exportCsv() API method that can be used like this:
19 | ```
20 | get->csv_export==1){
23 | $modules->get('ProcessChildrenCsvExport'); // load module
24 | // delimiter, enclosure, file extension, names in first row, multiple field separator, format values, pages to include (selector string), array of field names
25 | $page->exportCsv(',', '"', 'csv', true, "\r", true, 'include=all', array('title','body','images','textareas'));
26 | //$page->exportCsv() - this version uses the defaults from the module or page specific settings
27 | }
28 | // display content of template with link to same page with appended csv_export=1
29 | else{
30 | include("./head.inc");
31 |
32 | echo "Export Child Pages as CSV"; //link to initiate export
33 |
34 | include("./foot.inc");
35 | }
36 | ```
37 |
38 |
39 | ### Access permission
40 |
41 | This module requires a new permission: "batch-child-editor". This permission is created automatically on install and is added to the superuser role, but it is up to the developer to add the permission to other roles as required.
42 |
43 |
44 | ### Config Settings
45 |
46 | There are module-wide config settings, but these can be overwritten with page specific permissions which allows for highly customized creation and editing tools.
47 |
48 | * Which pages and templates will have the editor available and which can be separately configured.
49 | * Which edit modes should be availble to the user.
50 | * Alternate parent page - allows editing of external page tree.
51 | * Which data entry options (Text, Upload, URL link) should be availble to the user.
52 | * CSV import options.
53 | * CSV field pairings - really powerful for creating and updating pages - read more about it in the config settings.
54 | * CSV export options.
55 | * Whether the name of the page should also be changed along with the title. This is a very important setting and should be considered carefully, especially is the child pages are URL accessible.
56 | * Whether users can decide whether the name is also changed or not.
57 | * Whether to disable content protection for existing child pages and their children.
58 | * Trash or Delete.
59 | * Load Batch interface Open or Collapsed (open for quicker access).
60 | * Position interface (top, bottom, replace, new tab, inline fieldset).
61 | * Custom Title, Description, and Notes for each mode - allows you to tailor the editing interface specifically to your content editors and to specific content.
62 |
63 | #### Support forum:
64 | https://processwire.com/talk/topic/6102-batch-child-editor/
65 |
66 |
67 | ## License
68 |
69 | This program is free software; you can redistribute it and/or
70 | modify it under the terms of the GNU General Public License
71 | as published by the Free Software Foundation; either version 2
72 | of the License, or (at your option) any later version.
73 |
74 | This program is distributed in the hope that it will be useful,
75 | but WITHOUT ANY WARRANTY; without even the implied warranty of
76 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
77 | GNU General Public License for more details.
78 |
79 | You should have received a copy of the GNU General Public License
80 | along with this program; if not, write to the Free Software
81 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
82 |
83 | (See included LICENSE file for full license text.)
84 |
85 |
86 |
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/parsecsv-for-php/.editorconfig:
--------------------------------------------------------------------------------
1 | # @see http://editorconfig.org/
2 |
3 | # This is the top-most .editorconfig file; do not search in parent directories.
4 | root = true
5 |
6 | # All files.
7 | [*]
8 | indent_style = space
9 | indent_size = 4
10 | charset = utf-8
11 | trim_trailing_whitespace = true
12 | insert_final_newline = true
13 |
14 | [composer.json]
15 | indent_size = 4
16 |
17 | [.travis.yml]
18 | indent_size = 2
19 |
--------------------------------------------------------------------------------
/parsecsv-for-php/.gitignore:
--------------------------------------------------------------------------------
1 | *.bak
2 | /.idea
3 | /phive.xml
4 | /tools
5 | composer.lock
6 | vendor/
7 |
--------------------------------------------------------------------------------
/parsecsv-for-php/.travis.yml:
--------------------------------------------------------------------------------
1 | dist: trusty
2 | language: php
3 | dist: trusty
4 |
5 | php:
6 | - '7.4'
7 | - '7.3'
8 | - '7.2'
9 | - '7.1'
10 | - '7.0'
11 | - '5.6'
12 | - '5.5'
13 |
14 | before_install:
15 | - composer update
16 |
17 | script:
18 | - composer validate
19 | - vendor/bin/phpunit --version
20 | - vendor/bin/phpunit --configuration tests/phpunit.xml
21 |
22 | notifications:
23 | email:
24 | recipients:
25 | - will.knauss@gmail.com
26 | on_success: never
27 | on_failure: always
28 |
--------------------------------------------------------------------------------
/parsecsv-for-php/ChangeLog.txt:
--------------------------------------------------------------------------------
1 | ParseCSV 1.2.0
2 | -----------------------------------
3 | Date: 07-Jan-2020
4 |
5 | Breaking changes: none
6 |
7 | New features:
8 | - Compatible with PHP 7.4. Thanks to @andreybolonin
9 | @morrislaptop @martijnengler and @fjf2002.
10 | - unparse() now also understands $use_mb_convert_encoding.
11 | - Verbal condition operators are now allowed to contain
12 | upper case letters, for example:
13 | $csv->conditions = 'rating IS GREATER THAN 4';
14 |
15 | Bug fixes:
16 | - All filter condition operators containing "is" or "equals"
17 | were broken.
18 |
19 | Code quality:
20 | - Improved test coverage.
21 | -----------------------------------
22 |
23 |
24 | ParseCSV 1.1.1
25 | -----------------------------------
26 | Date: 2-Feb-2019
27 |
28 | Breaking changes: none
29 |
30 | New features: none
31 |
32 | Bug fixes:
33 | - Function load_data: check length of input, prevents E_NOTICE
34 | if too long.
35 | - Fixed bugs in unparse().
36 |
37 | Code quality:
38 | - Improved test coverage.
39 | -----------------------------------
40 |
41 |
42 | ParseCSV 1.1.0
43 | -----------------------------------
44 | Date: 9-Aug-2018
45 |
46 | Breaking changes:
47 | - Ignore entirely empty lines at the end of files
48 | See https://github.com/parsecsv/parsecsv-for-php/pull/142
49 | - Dropped support for PHP 5.4. Now, you need at leas PHP 5.5.
50 | - Fixed parse()'s return value: return true only if $data is useful.
51 |
52 | New features:
53 | - Added support for Laravel-style collections via the
54 | new getCollection() function - see
55 | https://github.com/parsecsv/parsecsv-for-php/pull/134
56 | - New function getTotalDataRowCount() - useful if
57 | $limit is set - see pull request #122.
58 | - Added requires to keep Composer-free environments working.
59 |
60 | Bug fixes:
61 | - Better support for streams.
62 | See https://github.com/parsecsv/parsecsv-for-php/pull/147
63 | - Fixed output() with custom header.
64 | See https://github.com/parsecsv/parsecsv-for-php/issues/132
65 | - Fixed bug on _validate_fields_for_unparse() if titles property
66 | is used instead of fields parameter for changing the titles for
67 | unparsing.
68 | - Fixed bug in unparse() that caused incorrect column order
69 | (Issue #41).
70 |
71 |
72 | Code quality:
73 | - Improved test coverage.
74 | -----------------------------------
75 |
76 |
77 | ParseCSV 1.0.0
78 | -----------------------------------
79 | Date: 3-March-2018
80 |
81 | - Renamed class from parseCSV to Csv and added name-
82 | space "ParseCsv" for PSR compliance.
83 |
84 | - Added support for MS Excel's "sep=" to detect the
85 | delimiter (Issue #60).
86 |
87 | - Added data type detection - function getDatatypes()
88 | guesses the type of each column.
89 |
90 | - MIME: output() sends correct MIME type to browser
91 | if the separator is a tab char (Issue #79).
92 |
93 | - Added support for mb_convert_encoding() instead of
94 | iconv() - see issue #109.
95 |
96 | - A number of minor bug fixes - see GitHub issues.
97 |
98 | - Added many more unit tests.
99 |
100 | -----------------------------------
101 |
102 |
103 | parseCSV 0.4.3 beta
104 | -----------------------------------
105 | Date: 1-July-2008
106 |
107 | - Issue #4. Added an option for setting sorting
108 | type behavior when sorting data.
109 | Simply set $csv->sort_type to "regular", "numeric",
110 | or "string".
111 |
112 | - Issue #6. Raw loaded file data is now cleared from
113 | file_data property when it has been successfully
114 | parsed to keep parseCSV's memory footprint to a
115 | minimum. Specifically handy when using multiple
116 | instances of parseCSV to process large files.
117 |
118 | -----------------------------------
119 |
120 |
121 | parseCSV 0.4.2 beta
122 | -----------------------------------
123 | Date: 31-May-2008
124 |
125 | - IMPORTANT! If you're using the output(),
126 | method please note that the first parameter
127 | has been completely removed as it was
128 | technically just useless. Instead, the second
129 | parameter (filename) doubles as its replacement.
130 | Simply put, if filename is not set or null, the
131 | output() method will not output a downloadable
132 | file. Please update your existing code
133 | when using 0.4.2 and later :)
134 |
135 | - Small fix to the headers sent by the output()
136 | method.
137 |
138 | - Added a download example using the output()
139 | method to the examples folder.
140 |
141 | -----------------------------------
142 |
143 |
144 | parseCSV 0.4.1 beta
145 | -----------------------------------
146 | Date: 29-May-2008
147 |
148 | - Fixed a small bug in how the output() method
149 | handles input data.
150 |
151 | -----------------------------------
152 |
153 |
154 | parseCSV 0.4 beta
155 | -----------------------------------
156 | Date: 11-Apr-2008
157 |
158 | - Error reporting for files/data which is corrupt
159 | or has formatting errors like using double
160 | quotes in a field without enclosing quotes. Or
161 | not escaping double quotes with a second one.
162 |
163 | - parse() method does not require input anymore
164 | if the "$object->file" property has been set.
165 |
166 | I'm calling this a beta release due to the heavy
167 | modifications to the core parsing logic required
168 | for error reporting to work. I have tested the
169 | new code quite extensively, I'm fairly confident
170 | that it still parses exactly as it always has.
171 |
172 | The second reason I'm calling it a beta release
173 | is cause I'm sure the error reporting code will
174 | need more refinements and tweaks to detect more
175 | types of errors, as it's only picking two types
176 | or syntax errors right now. However, it seems
177 | these two are the most common errors that you
178 | would be likely to come across.
179 |
180 | -----------------------------------
181 |
182 |
183 | parseCSV 0.3.2
184 | -----------------------------------
185 | Date: 1-Apr-2008
186 |
187 | This is primarily a bug-fix release for a critical
188 | bug which was brought to my attention.
189 |
190 | - Fixed a critical bug in conditions parsing which
191 | would generate corrupt matching patterns causing
192 | the condition(s) to not work at all in some
193 | situations.
194 |
195 | - Fixed a small code error which would cause PHP to
196 | generate a invalid offset notice when zero length
197 | values were fed into the unparse() method to
198 | generate CSV data from an array.
199 |
200 | Notice: If you have been using the "parsecsv-stable"
201 | branch as an external in any of your projects,
202 | please use the "stable/parsecsv" branch from this
203 | point on as I will eventually remove the former due
204 | to it's stupid naming.
205 |
206 | -----------------------------------
207 |
208 |
209 | parseCSV 0.3.1
210 | -----------------------------------
211 | Date: 1-Sep-2007
212 |
213 | - Small change to default output settings to
214 | conform with RFC 4180 (http://rfc.net/rfc4180.html).
215 | Only the LF (line feed) character was used
216 | by default to separate rows, rather than
217 | CRLF (carriage return & line feed).
218 |
219 | -----------------------------------
220 |
221 |
222 | parseCSV 0.3.0
223 | -----------------------------------
224 | Date: 9-Aug-2007
225 |
226 | - Changed to the MIT license.
227 |
228 | - Added offset and limit options.
229 |
230 | - Added SQL-like conditions for quickly
231 | filtering out entries. Documentation on the
232 | condition syntax is forthcoming.
233 |
234 | - Small parsing modification to comply
235 | with some recent changes to the specifications
236 | outlined on Wikipedia's Comma-separated values
237 | article.
238 |
239 | - Minor changes and optimizations, and a few
240 | spelling corrections. Oops :)
241 |
242 | - Included more complex code examples in the
243 | parseCSV download.
244 |
245 | -----------------------------------
246 |
247 |
248 | parseCSV 0.2.1
249 | -----------------------------------
250 | Date: 8-Aug-2007
251 |
252 | - Fixed stupid code which caused auto function
253 | to not work in some situations.
254 |
255 | -----------------------------------
256 |
257 |
258 | parseCSV 0.2.0 beta
259 | -----------------------------------
260 | Date: 2-Jan-2007
261 |
262 | - Added auto() function to automatically detect
263 | delimiter character.
264 | Useful for user upload in case delimiter is
265 | comma (,), tab, or semi-colon (;). Some
266 | versions of MS Excel for Windows use
267 | semi-colons instead of commas when saving to
268 | CSV files.
269 | It uses a process of elimination to eliminate
270 | characters that can not be the delimiter,
271 | so it should work on all CSV-structured files
272 | almost no matter what the delimiter is.
273 |
274 | - Generally updated some of the core workings
275 | to increase performance, and offer better
276 | support for large (1MB and up) files.
277 |
278 | - Added code examples to header comment.
279 |
280 | -----------------------------------
281 |
282 |
283 | parseCSV 0.1.6 beta
284 | -----------------------------------
285 | Date: 22-Dec-2006
286 |
287 | - Updated output() function.
288 |
289 | -----------------------------------
290 |
291 |
292 | parseCSV 0.1.5 beta
293 | -----------------------------------
294 | Date: 22-Dec-2006
295 |
296 | - Added output() function for easy output to
297 | browser, for downloading features for example.
298 |
299 | -----------------------------------
300 |
301 |
302 | parseCSV 0.1.4 beta
303 | -----------------------------------
304 | Date: 17-Dec-2006
305 |
306 | - Minor changes and fixes
307 |
308 | -----------------------------------
309 |
310 |
311 | parseCSV 0.1.3 beta
312 | -----------------------------------
313 | Date: 17-Dec-2006
314 |
315 | - Added GPL v2.0 license.
316 |
317 | -----------------------------------
318 |
319 |
320 | parseCSV 0.1.2 beta
321 | -----------------------------------
322 | Date: 17-Dec-2006
323 |
324 | - Added encoding() function for easier character
325 | encoding configuration.
326 |
327 | -----------------------------------
328 |
329 |
330 | parseCSV 0.1.1 beta
331 | -----------------------------------
332 | Date: 24-Nov-2006
333 |
334 | - Added support for a PHP die command on first
335 | line of csv files if they have a .php extension
336 | to protect secure data from being displayed
337 | directly to the browser.
338 |
339 | -----------------------------------
340 |
341 |
342 | parseCSV 0.1 beta
343 | -----------------------------------
344 | Date: 23-Nov-2006
345 |
346 | - Initial release
347 |
348 | -----------------------------------
349 |
--------------------------------------------------------------------------------
/parsecsv-for-php/License.txt:
--------------------------------------------------------------------------------
1 | (The MIT license)
2 |
3 | Copyright (c) 2014 Jim Myhrberg.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/parsecsv-for-php/Makefile:
--------------------------------------------------------------------------------
1 | COMPOSER_BIN_DIR := vendor/bin
2 | PHPUNIT_ARGS = -c tests/phpunit.xml
3 |
4 | test: phpunit-dep
5 | ${COMPOSER_BIN_DIR}/phpunit ${PHPUNIT_ARGS}
6 |
7 | phpunit-dep:
8 | test -f ${COMPOSER_BIN_DIR}/phpunit || ( \
9 | echo "phpunit is required to run tests." \
10 | "Please run: composer install" >&2 && \
11 | exit 1 \
12 | )
13 |
14 | .SILENT:
15 | .PHONY: test phpunit-dep
16 |
--------------------------------------------------------------------------------
/parsecsv-for-php/README.md:
--------------------------------------------------------------------------------
1 | # ParseCsv
2 | [](https://opencollective.com/parsecsv)
3 |
4 | ParseCsv is an easy-to-use PHP class that reads and writes CSV data properly. It
5 | fully conforms to the specifications outlined on the on the
6 | [Wikipedia article][CSV] (and thus RFC 4180). It has many advanced features which help make your
7 | life easier when dealing with CSV data.
8 |
9 | You may not need a library at all: before using ParseCsv, please make sure if PHP's own `str_getcsv()`, ``fgetcvs()`` or `fputcsv()` meets your needs.
10 |
11 | This library was originally created in early 2007 by [jimeh](https://github.com/jimeh) due to the lack of built-in
12 | and third-party support for handling CSV data in PHP.
13 |
14 | [csv]: http://en.wikipedia.org/wiki/Comma-separated_values
15 |
16 | ## Features
17 |
18 | * ParseCsv is a complete and fully featured CSV solution for PHP
19 | * Supports enclosed values, enclosed commas, double quotes and new lines.
20 | * Automatic delimiter character detection.
21 | * Sort data by specific fields/columns.
22 | * Easy data manipulation.
23 | * Basic SQL-like _conditions_, _offset_ and _limit_ options for filtering
24 | data.
25 | * Error detection for incorrectly formatted input. It attempts to be
26 | intelligent, but can not be trusted 100% due to the structure of CSV, and
27 | how different programs like Excel for example outputs CSV data.
28 | * Support for character encoding conversion using PHP's
29 | `iconv()` and `mb_convert_encoding()` functions.
30 | * Supports PHP 5.5 and higher.
31 | It certainly works with PHP 7.2 and all versions in between.
32 |
33 | ## Installation
34 |
35 | Installation is easy using Composer. Just run the following on the
36 | command line:
37 | ```
38 | composer require parsecsv/php-parsecsv
39 | ```
40 |
41 | If you don't use a framework such as Drupal, Laravel, Symfony, Yii etc.,
42 | you may have to manually include Composer's autoloader file in your PHP
43 | script:
44 | ```php
45 | require_once __DIR__ . '/vendor/autoload.php';
46 | ```
47 |
48 | #### Without composer
49 | Not recommended, but technically possible: you can also clone the
50 | repository or extract the
51 | [ZIP](https://github.com/parsecsv/parsecsv-for-php/archive/master.zip).
52 | To use ParseCSV, you then have to add a `require 'parsecsv.lib.php';` line.
53 |
54 | ## Example Usage
55 |
56 | **General**
57 |
58 | ```php
59 | $csv = new ParseCsv\Csv('data.csv');
60 | print_r($csv->data);
61 | ```
62 |
63 | **Tab delimited, and encoding conversion**
64 |
65 | ```php
66 | $csv = new ParseCsv\Csv();
67 | $csv->encoding('UTF-16', 'UTF-8');
68 | $csv->delimiter = "\t";
69 | $csv->parse('data.tsv');
70 | print_r($csv->data);
71 | ```
72 |
73 | **Auto-detect delimiter character**
74 |
75 | ```php
76 | $csv = new ParseCsv\Csv();
77 | $csv->auto('data.csv');
78 | print_r($csv->data);
79 | ```
80 |
81 | **Parse data with offset**
82 | * ignoring the first X (e.g. two) rows
83 | ```php
84 | $csv = new ParseCsv\Csv();
85 | $csv->offset = 2;
86 | $csv->parse('data.csv');
87 | print_r($csv->data);
88 | ```
89 |
90 | **Limit the number of returned data rows**
91 | ```php
92 | $csv = new ParseCsv\Csv();
93 | $csv->limit = 5;
94 | $csv->parse('data.csv');
95 | print_r($csv->data);
96 | ```
97 |
98 | **Get total number of data rows without parsing whole data**
99 | * Excluding heading line if present (see $csv->header property)
100 | ```php
101 | $csv = new ParseCsv\Csv();
102 | $csv->load_data('data.csv');
103 | $count = $csv->getTotalDataRowCount();
104 | print_r($count);
105 | ```
106 |
107 | **Get most common data type for each column (Requires PHP >= 5.5)**
108 |
109 | ```php
110 | $csv = new ParseCsv\Csv('data.csv');
111 | $csv->getDatatypes()
112 | print_r($csv->data_types);
113 | ```
114 |
115 | **Modify data in a CSV file**
116 |
117 | Change data values:
118 | ```php
119 | $csv = new ParseCsv\Csv();
120 | $csv->sort_by = 'id';
121 | $csv->parse('data.csv');
122 | # "4" is the value of the "id" column of the CSV row
123 | $csv->data[4] = array('firstname' => 'John', 'lastname' => 'Doe', 'email' => 'john@doe.com');
124 | $csv->save();
125 | ```
126 |
127 | Enclose each data value by quotes:
128 | ```php
129 | $csv = new ParseCsv\Csv();
130 | $csv->parse('data.csv');
131 | $csv->enclose_all = true;
132 | $csv->save();
133 | ```
134 |
135 | **Replace field names or set ones if missing**
136 |
137 | ```php
138 | $csv = new ParseCsv\Csv();
139 | $csv->fields = ['id', 'name', 'category']
140 | $csv->parse('data.csv');
141 | ```
142 |
143 | **Add row/entry to end of CSV file**
144 |
145 | _Only recommended when you know the exact structure of the file._
146 |
147 | ```php
148 | $csv = new ParseCsv\Csv();
149 | $csv->save('data.csv', array(array('1986', 'Home', 'Nowhere', '')), true);
150 | ```
151 |
152 | **Convert 2D array to CSV data and send headers to browser to treat output as
153 | a file and download it**
154 |
155 | ```php
156 | $csv = new ParseCsv\Csv();
157 | $csv->output('movies.csv', $array, array('field 1', 'field 2'), ',');
158 | ```
159 |
160 | For more complex examples, see the ``tests`` and `examples` directories.
161 |
162 | ## Credits
163 |
164 | * ParseCsv is based on the concept of [Ming Hong Ng][ming]'s [CsvFileParser][]
165 | class.
166 |
167 | [ming]: http://minghong.blogspot.com/
168 | [CsvFileParser]: http://minghong.blogspot.com/2006/07/csv-parser-for-php.html
169 |
170 |
171 | ## Contributors
172 |
173 | Please find a complete list on the project's [contributors][] page.
174 |
175 | [contributors]: https://github.com/parsecsv/parsecsv-for-php/graphs/contributors
176 |
177 |
178 |
179 | ## Contributors
180 |
181 | ### Code Contributors
182 |
183 | This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
184 |
185 |
186 | ### Financial Contributors
187 |
188 | Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/parsecsv/contribute)]
189 |
190 | #### Individuals
191 |
192 |
193 |
194 | #### Organizations
195 |
196 | Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/parsecsv/contribute)]
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 | ## License
210 |
211 | (The MIT license)
212 |
213 | Copyright (c) 2014 Jim Myhrberg.
214 |
215 | Permission is hereby granted, free of charge, to any person obtaining a copy
216 | of this software and associated documentation files (the "Software"), to deal
217 | in the Software without restriction, including without limitation the rights
218 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
219 | copies of the Software, and to permit persons to whom the Software is
220 | furnished to do so, subject to the following conditions:
221 |
222 | The above copyright notice and this permission notice shall be included in all
223 | copies or substantial portions of the Software.
224 |
225 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
226 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
227 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
228 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
229 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
230 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
231 | SOFTWARE.
232 |
233 | [](https://travis-ci.org/parsecsv/parsecsv-for-php)
234 |
--------------------------------------------------------------------------------
/parsecsv-for-php/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "parsecsv/php-parsecsv",
3 | "description": "CSV data parser for PHP",
4 | "license": "MIT",
5 | "authors": [
6 | {
7 | "name": "Jim Myhrberg",
8 | "email": "contact@jimeh.me"
9 | },
10 | {
11 | "name": "William Knauss",
12 | "email": "will.knauss@gmail.com"
13 | },
14 | {
15 | "name": "Susann Sgorzaly",
16 | "homepage": "https://github.com/susgo"
17 | },
18 | {
19 | "name": "Christian Bläul",
20 | "homepage": "https://github.com/Fonata"
21 | }
22 | ],
23 | "autoload": {
24 | "psr-4": {
25 | "ParseCsv\\": "src"
26 | }
27 | },
28 | "autoload-dev": {
29 | "psr-4": {
30 | "ParseCsv\\tests\\": "tests"
31 | }
32 | },
33 | "require": {
34 | "php": ">=5.5"
35 | },
36 | "require-dev": {
37 | "phpunit/phpunit": "4.1.*"
38 | },
39 | "suggest": {
40 | "illuminate/support": "Fluent array interface for map functions"
41 | },
42 | "extra": {
43 | "branch-alias": {
44 | "dev-master": "1.0.x-dev"
45 | }
46 | },
47 | "support": {
48 | "issues": "https://github.com/parsecsv/parsecsv-for-php/issues",
49 | "source": "https://github.com/parsecsv/parsecsv-for-php"
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/parsecsv-for-php/examples/_books.csv:
--------------------------------------------------------------------------------
1 | rating,title,author,type,asin,tags,review
2 | 0,The Killing Kind,John Connolly,Book,0340771224,,i still haven't had time to read this one...
3 | 0,The Third Secret,Steve Berry,Book,0340899263,,need to find time to read this book
4 | 3,The Last Templar,Raymond Khoury,Book,0752880705,,
5 | 5,The Traveller,John Twelve Hawks,Book,059305430X,,
6 | 4,Crisis Four,Andy Mcnab,Book,0345428080,,
7 | 5,Prey,Michael Crichton,Book,0007154534,,
8 | 3,The Broker (Paperback),John Grisham,Book,0440241588,book johngrisham,"good book, but is slow in the middle"
9 | 3,Without Blood (Paperback),Alessandro Baricco,Book,1841955744,,
10 | 5,State of Fear (Paperback),Michael Crichton,Book,0061015733,,
11 | 4,The Rule of Four (Paperback),Ian Caldwell,Book,0099451956,book bestseller,
12 | 4,Deception Point (Paperback),Dan Brown,Book,0671027387,book danbrown bestseller,
13 | 5,Digital Fortress : A Thriller (Mass Market Paperback),Dan Brown,Book,0312995423,book danbrown bestseller,
14 | 5,Angels & Demons (Mass Market Paperback),Dan Brown,Book,0671027360,book danbrown bestseller,
15 | 4,The Da Vinci Code (Hardcover),Dan Brown," Book ",0385504209,book movie danbrown bestseller davinci,
--------------------------------------------------------------------------------
/parsecsv-for-php/examples/basic.php:
--------------------------------------------------------------------------------
1 |
2 | auto('_books.csv');
16 |
17 | # ...or if you know the delimiter, set the delimiter character
18 | # if its not the default comma...
19 | // $csv->delimiter = "\t"; # tab delimited
20 |
21 | # ...and then use the parse() function.
22 | // $csv->parse('_books.csv');
23 |
24 |
25 | # Output result.
26 | // print_r($csv->data);
27 |
28 |
29 | ?>
30 |
2 | conditions = 'title contains paperback OR title contains hardcover';
17 | $csv->conditions = 'author does not contain dan brown';
18 | // $csv->conditions = 'rating < 4 OR author is John Twelve Hawks';
19 | // $csv->conditions = 'rating > 4 AND author is Dan Brown';
20 | // $csv->conditions = 'rating is greater than 4';
21 |
22 |
23 | # Parse '_books.csv' using automatic delimiter detection.
24 | $csv->auto('_books.csv');
25 |
26 |
27 | # Output result.
28 | // print_r($csv->data);
29 |
30 |
31 | ?>
32 |
33 |
46 |
47 |
48 | titles as $value): ?>
49 |
50 |
51 |
52 | data as $key => $row): ?>
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/parsecsv-for-php/examples/download.php:
--------------------------------------------------------------------------------
1 | auto('_books.csv');
16 |
17 | # ...or if you know the delimiter, set the delimiter character
18 | # if its not the default comma...
19 | // $csv->delimiter = "\t"; # tab delimited
20 |
21 | # ...and then use the parse() function.
22 | // $csv->parse('_books.csv');
23 |
24 | # now we have data in $csv->data, at which point we can modify
25 | # it to our hearts content, like removing the last item...
26 | array_pop($csv->data);
27 |
28 | # then we output the file to the browser as a downloadable file...
29 | $csv->output('books.csv');
30 | # ...when the first parameter is given and is not null, the
31 | # output method will itself send the correct headers and the
32 | # data to download the output as a CSV file. if it's not set
33 | # or is set to null, output will only return the generated CSV
34 | # output data, and will not output to the browser itself.
35 |
--------------------------------------------------------------------------------
/parsecsv-for-php/examples/limit.php:
--------------------------------------------------------------------------------
1 |
2 | sort_by = 'title';
25 |
26 |
27 | # offset from the beginning of the file,
28 | # ignoring the first X number of rows.
29 | $csv->offset = 2;
30 |
31 | # limit the number of returned rows.
32 | $csv->limit = 3;
33 |
34 |
35 | # Parse '_books.csv' using automatic delimiter detection.
36 | $csv->auto('_books.csv');
37 |
38 |
39 | # Output result.
40 | // print_r($csv->data);
41 |
42 |
43 | ?>
44 |
45 |
58 |
59 |
60 | titles as $value): ?>
61 |
62 |
63 |
64 | data as $key => $row): ?>
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/parsecsv-for-php/examples/save_to_file_without_header_row.php:
--------------------------------------------------------------------------------
1 | heading = false;
15 |
16 | # Specify which columns to write, and in which order.
17 | # We won't output the 'Awesome' column this time.
18 | $csv->titles = ['Age', 'Name'];
19 |
20 | # Data to write:
21 | $csv->data = [
22 | 0 => ['Name' => 'Anne', 'Age' => 45, 'Awesome' => true],
23 | 1 => ['Name' => 'John', 'Age' => 44, 'Awesome' => false],
24 | ];
25 |
26 | # Then we save the file to the file system:
27 | $csv->save('people.csv');
28 |
--------------------------------------------------------------------------------
/parsecsv-for-php/parsecsv.lib.php:
--------------------------------------------------------------------------------
1 | var_name = 'value';
49 | */
50 |
51 | /**
52 | * Heading
53 | * Use first line/entry as field names
54 | *
55 | * @var bool
56 | */
57 | public $heading = true;
58 |
59 | /**
60 | * Fields
61 | * Override field names
62 | *
63 | * @var array
64 | */
65 | public $fields = array();
66 |
67 | /**
68 | * Sort By
69 | * Sort CSV by this field
70 | *
71 | * @var string|null
72 | */
73 | public $sort_by = null;
74 |
75 | /**
76 | * Sort Reverse
77 | * Reverse the sort function
78 | *
79 | * @var bool
80 | */
81 | public $sort_reverse = false;
82 |
83 | /**
84 | * Sort Type
85 | * Sort behavior passed to sort methods
86 | *
87 | * regular = SORT_REGULAR
88 | * numeric = SORT_NUMERIC
89 | * string = SORT_STRING
90 | *
91 | * @var string|null
92 | */
93 | public $sort_type = SortEnum::SORT_TYPE_REGULAR;
94 |
95 | /**
96 | * Delimiter
97 | * Delimiter character
98 | *
99 | * @var string
100 | */
101 | public $delimiter = ',';
102 |
103 | /**
104 | * Enclosure
105 | * Enclosure character
106 | *
107 | * @var string
108 | */
109 | public $enclosure = '"';
110 |
111 | /**
112 | * Enclose All
113 | * Force enclosing all columns
114 | *
115 | * @var bool
116 | */
117 | public $enclose_all = false;
118 |
119 | /**
120 | * Conditions
121 | * Basic SQL-Like conditions for row matching
122 | *
123 | * @var string|null
124 | */
125 | public $conditions = null;
126 |
127 | /**
128 | * Offset
129 | * Number of rows to ignore from beginning of data. If present, the heading
130 | * row is also counted (if $this->heading == true). In other words,
131 | * $offset == 1 and $offset == 0 have the same meaning in that situation.
132 | *
133 | * @var int|null
134 | */
135 | public $offset = null;
136 |
137 | /**
138 | * Limit
139 | * Limits the number of returned rows to the specified amount
140 | *
141 | * @var int|null
142 | */
143 | public $limit = null;
144 |
145 | /**
146 | * Auto Depth
147 | * Number of rows to analyze when attempting to auto-detect delimiter
148 | *
149 | * @var int
150 | */
151 | public $auto_depth = 15;
152 |
153 | /**
154 | * Auto Non Chars
155 | * Characters that should be ignored when attempting to auto-detect delimiter
156 | *
157 | * @var string
158 | */
159 | public $auto_non_chars = "a-zA-Z0-9\n\r";
160 |
161 | /**
162 | * Auto Preferred
163 | * preferred delimiter characters, only used when all filtering method
164 | * returns multiple possible delimiters (happens very rarely)
165 | *
166 | * @var string
167 | */
168 | public $auto_preferred = ",;\t.:|";
169 |
170 | /**
171 | * Convert Encoding
172 | * Should we convert the CSV character encoding?
173 | *
174 | * @var bool
175 | */
176 | public $convert_encoding = false;
177 |
178 | /**
179 | * Input Encoding
180 | * Set the input encoding
181 | *
182 | * @var string
183 | */
184 | public $input_encoding = 'ISO-8859-1';
185 |
186 | /**
187 | * Output Encoding
188 | * Set the output encoding
189 | *
190 | * @var string
191 | */
192 | public $output_encoding = 'ISO-8859-1';
193 |
194 | /**
195 | * Whether to use mb_convert_encoding() instead of iconv().
196 | *
197 | * The former is platform-independent whereas the latter is the traditional
198 | * default go-to solution.
199 | *
200 | * @var bool (if false, iconv() is used)
201 | */
202 | public $use_mb_convert_encoding = false;
203 |
204 | /**
205 | * Linefeed
206 | * Line feed characters used by unparse, save, and output methods
207 | *
208 | * @var string
209 | */
210 | public $linefeed = "\r";
211 |
212 | /**
213 | * Output Delimiter
214 | * Sets the output delimiter used by the output method
215 | *
216 | * @var string
217 | */
218 | public $output_delimiter = ',';
219 |
220 | /**
221 | * Output filename
222 | * Sets the output filename
223 | *
224 | * @var string
225 | */
226 | public $output_filename = 'data.csv';
227 |
228 | /**
229 | * Keep File Data
230 | * keep raw file data in memory after successful parsing (useful for debugging)
231 | *
232 | * @var bool
233 | */
234 | public $keep_file_data = false;
235 |
236 | /**
237 | * Internal variables
238 | */
239 |
240 | /**
241 | * File
242 | * Current Filename
243 | *
244 | * @var string
245 | */
246 | public $file;
247 |
248 | /**
249 | * File Data
250 | * Current file data
251 | *
252 | * @var string
253 | */
254 | public $file_data;
255 |
256 | /**
257 | * Error
258 | * Contains the error code if one occurred
259 | *
260 | * 0 = No errors found. Everything should be fine :)
261 | * 1 = Hopefully correctable syntax error was found.
262 | * 2 = Enclosure character (double quote by default)
263 | * was found in non-enclosed field. This means
264 | * the file is either corrupt, or does not
265 | * standard CSV formatting. Please validate
266 | * the parsed data yourself.
267 | *
268 | * @var int
269 | */
270 | public $error = 0;
271 |
272 | /**
273 | * Error Information
274 | * Detailed error information
275 | *
276 | * @var array
277 | */
278 | public $error_info = array();
279 |
280 | /**
281 | * $titles has 4 distinct tasks:
282 | * 1. After reading in CSV data, $titles will contain the column headers
283 | * present in the data.
284 | *
285 | * 2. It defines which fields from the $data array to write e.g. when
286 | * calling unparse(), and in which order. This lets you skip columns you
287 | * don't want in your output, but are present in $data.
288 | * See examples/save_to_file_without_header_row.php.
289 | *
290 | * 3. It lets you rename columns. See StreamTest::testWriteStream for an
291 | * example.
292 | *
293 | * 4. When writing data and $header is true, then $titles is also used for
294 | * the first row.
295 | *
296 | * @var array
297 | */
298 | public $titles = array();
299 |
300 | /**
301 | * Data
302 | * Two-dimensional array of CSV data
303 | *
304 | * @var array
305 | */
306 | public $data = array();
307 |
308 | use DatatypeTrait;
309 |
310 | /**
311 | * Constructor
312 | * Class constructor
313 | *
314 | * @param string|null $input The CSV string or a direct file path
315 | * @param integer|null $offset Number of rows to ignore from the
316 | * beginning of the data
317 | * @param integer|null $limit Limits the number of returned rows
318 | * to specified amount
319 | * @param string|null $conditions Basic SQL-like conditions for row
320 | * matching
321 | * @param null|true $keep_file_data Keep raw file data in memory after
322 | * successful parsing
323 | * (useful for debugging)
324 | */
325 | public function __construct($input = null, $offset = null, $limit = null, $conditions = null, $keep_file_data = null) {
326 | $this->init($offset, $limit, $conditions, $keep_file_data);
327 |
328 | if (!empty($input)) {
329 | $this->parse($input);
330 | }
331 | }
332 |
333 | /**
334 | * @param integer|null $offset Number of rows to ignore from the
335 | * beginning of the data
336 | * @param integer|null $limit Limits the number of returned rows
337 | * to specified amount
338 | * @param string|null $conditions Basic SQL-like conditions for row
339 | * matching
340 | * @param null|true $keep_file_data Keep raw file data in memory after
341 | * successful parsing
342 | * (useful for debugging)
343 | */
344 | public function init($offset = null, $limit = null, $conditions = null, $keep_file_data = null) {
345 | if (!is_null($offset)) {
346 | $this->offset = $offset;
347 | }
348 |
349 | if (!is_null($limit)) {
350 | $this->limit = $limit;
351 | }
352 |
353 | if (!is_null($conditions)) {
354 | $this->conditions = $conditions;
355 | }
356 |
357 | if (!is_null($keep_file_data)) {
358 | $this->keep_file_data = $keep_file_data;
359 | }
360 | }
361 |
362 | // ==============================================
363 | // ----- [ Main Functions ] ---------------------
364 | // ==============================================
365 |
366 | /**
367 | * Parse
368 | * Parse a CSV file or string
369 | *
370 | * @param string|null $input The CSV string or a direct file path
371 | * @param integer $offset Number of rows to ignore from the
372 | * beginning of the data
373 | * @param integer $limit Limits the number of returned rows to
374 | * specified amount
375 | * @param string $conditions Basic SQL-like conditions for row
376 | * matching
377 | *
378 | * @return bool True on success
379 | */
380 | public function parse($input = null, $offset = null, $limit = null, $conditions = null) {
381 | if (is_null($input)) {
382 | $input = $this->file;
383 | }
384 |
385 | if (empty($input)) {
386 | return false;
387 | }
388 |
389 | $this->init($offset, $limit, $conditions);
390 |
391 | if (strlen($input) <= PHP_MAXPATHLEN && is_readable($input)) {
392 | $this->file = $input;
393 | $this->data = $this->_parse_file();
394 | } else {
395 | $this->file = null;
396 | $this->file_data = &$input;
397 | $this->data = $this->_parse_string();
398 | }
399 |
400 | return $this->data !== false;
401 | }
402 |
403 | /**
404 | * Save
405 | * Save changes, or write a new file and/or data
406 | *
407 | * @param string $file File location to save to
408 | * @param array $data 2D array of data
409 | * @param bool $append Append current data to end of target CSV, if file
410 | * exists
411 | * @param array $fields Field names. Sets the header. If it is not set
412 | * $this->titles would be used instead.
413 | *
414 | * @return bool
415 | * True on success
416 | */
417 | public function save($file = '', $data = array(), $append = FileProcessingModeEnum::MODE_FILE_OVERWRITE, $fields = array()) {
418 | if (empty($file)) {
419 | $file = &$this->file;
420 | }
421 |
422 | $mode = FileProcessingModeEnum::getAppendMode($append);
423 | $is_php = preg_match('/\.php$/i', $file) ? true : false;
424 |
425 | return $this->_wfile($file, $this->unparse($data, $fields, $append, $is_php), $mode);
426 | }
427 |
428 | /**
429 | * Output
430 | * Generate a CSV based string for output.
431 | *
432 | * @param string|null $filename If a filename is specified here or in the
433 | * object, headers and data will be output
434 | * directly to browser as a downloadable
435 | * file. This file doesn't have to exist on
436 | * the server; the parameter only affects
437 | * how the download is called to the
438 | * browser.
439 | * @param array[] $data 2D array with data
440 | * @param array $fields Field names
441 | * @param string|null $delimiter character used to separate data
442 | *
443 | * @return string The resulting CSV string
444 | */
445 | public function output($filename = null, $data = array(), $fields = array(), $delimiter = null) {
446 | if (empty($filename)) {
447 | $filename = $this->output_filename;
448 | }
449 |
450 | if ($delimiter === null) {
451 | $delimiter = $this->output_delimiter;
452 | }
453 |
454 | $flat_string = $this->unparse($data, $fields, null, null, $delimiter);
455 |
456 | if (!is_null($filename)) {
457 | $mime = $delimiter === "\t" ?
458 | 'text/tab-separated-values' :
459 | 'application/csv';
460 | header('Content-type: ' . $mime);
461 | header('Content-Length: ' . strlen($flat_string));
462 | header('Cache-Control: no-cache, must-revalidate');
463 | header('Pragma: no-cache');
464 | header('Expires: 0');
465 | header('Content-Disposition: attachment; filename="' . $filename . '"; modification-date="' . date('r') . '";');
466 |
467 | echo $flat_string;
468 | }
469 |
470 | return $flat_string;
471 | }
472 |
473 | /**
474 | * Encoding
475 | * Convert character encoding
476 | *
477 | * @param string $input Input character encoding, uses default if left blank
478 | * @param string $output Output character encoding, uses default if left blank
479 | */
480 | public function encoding($input = null, $output = null) {
481 | $this->convert_encoding = true;
482 | if (!is_null($input)) {
483 | $this->input_encoding = $input;
484 | }
485 |
486 | if (!is_null($output)) {
487 | $this->output_encoding = $output;
488 | }
489 | }
490 |
491 | /**
492 | * Auto
493 | * Auto-Detect Delimiter: Find delimiter by analyzing a specific number of
494 | * rows to determine most probable delimiter character
495 | *
496 | * @param string|null $file Local CSV file
497 | * @param bool $parse True/false parse file directly
498 | * @param int $search_depth Number of rows to analyze
499 | * @param string $preferred Preferred delimiter characters
500 | * @param string|null $enclosure Enclosure character, default is double quote (").
501 | *
502 | * @return string The detected field delimiter
503 | */
504 | public function auto($file = null, $parse = true, $search_depth = null, $preferred = null, $enclosure = null) {
505 | if (is_null($file)) {
506 | $file = $this->file;
507 | }
508 |
509 | if (empty($search_depth)) {
510 | $search_depth = $this->auto_depth;
511 | }
512 |
513 | if (is_null($enclosure)) {
514 | $enclosure = $this->enclosure;
515 | } else {
516 | $this->enclosure = $enclosure;
517 | }
518 |
519 | if (is_null($preferred)) {
520 | $preferred = $this->auto_preferred;
521 | }
522 |
523 | if (empty($this->file_data)) {
524 | if ($this->_check_data($file)) {
525 | $data = &$this->file_data;
526 | } else {
527 | return false;
528 | }
529 | } else {
530 | $data = &$this->file_data;
531 | }
532 |
533 | if (!$this->_detect_and_remove_sep_row_from_data($data)) {
534 | $this->_guess_delimiter($search_depth, $preferred, $enclosure, $data);
535 | }
536 |
537 | // parse data
538 | if ($parse) {
539 | $this->data = $this->_parse_string();
540 | }
541 |
542 | return $this->delimiter;
543 | }
544 |
545 | /**
546 | * Get total number of data rows (exclusive heading line if present) in CSV
547 | * without parsing the whole data string.
548 | *
549 | * @return bool|int
550 | */
551 | public function getTotalDataRowCount() {
552 | if (empty($this->file_data)) {
553 | return false;
554 | }
555 |
556 | $data = $this->file_data;
557 |
558 | $this->_detect_and_remove_sep_row_from_data($data);
559 |
560 | $pattern = sprintf('/%1$s[^%1$s]*%1$s/i', $this->enclosure);
561 | preg_match_all($pattern, $data, $matches);
562 |
563 | /** @var array[] $matches */
564 | foreach ($matches[0] as $match) {
565 | if (empty($match) || (strpos($match, $this->enclosure) === false)) {
566 | continue;
567 | }
568 |
569 | $replace = str_replace(["\r", "\n"], '', $match);
570 | $data = str_replace($match, $replace, $data);
571 | }
572 |
573 | $headingRow = $this->heading ? 1 : 0;
574 |
575 | $count = substr_count($data, "\r")
576 | + substr_count($data, "\n")
577 | - substr_count($data, "\r\n")
578 | - $headingRow;
579 |
580 | return $count;
581 | }
582 |
583 | // ==============================================
584 | // ----- [ Core Functions ] ---------------------
585 | // ==============================================
586 |
587 | /**
588 | * Parse File
589 | * Read file to string and call _parse_string()
590 | *
591 | * @param string|null $file Local CSV file
592 | *
593 | * @return array|bool
594 | */
595 | protected function _parse_file($file = null) {
596 | if (is_null($file)) {
597 | $file = $this->file;
598 | }
599 |
600 | if (empty($this->file_data)) {
601 | $this->load_data($file);
602 | }
603 |
604 | return !empty($this->file_data) ? $this->_parse_string() : false;
605 | }
606 |
607 | /**
608 | * Internal function to parse CSV strings to arrays.
609 | *
610 | * If you need BOM detection or character encoding conversion, please call
611 | * $csv->load_data($your_data_string) first, followed by a call to
612 | * $csv->parse($csv->file_data).
613 | *
614 | * To detect field separators, please use auto() instead.
615 | *
616 | * @param string $data CSV data
617 | *
618 | * @return array|false - 2D array with CSV data, or false on failure
619 | */
620 | protected function _parse_string($data = null) {
621 | if (empty($data)) {
622 | if ($this->_check_data()) {
623 | $data = &$this->file_data;
624 | } else {
625 | return false;
626 | }
627 | }
628 |
629 | $white_spaces = str_replace($this->delimiter, '', " \t\x0B\0");
630 |
631 | $rows = array();
632 | $row = array();
633 | $row_count = 0;
634 | $current = '';
635 | $head = !empty($this->fields) ? $this->fields : array();
636 | $col = 0;
637 | $enclosed = false;
638 | $was_enclosed = false;
639 | $strlen = strlen($data);
640 |
641 | // force the parser to process end of data as a character (false) when
642 | // data does not end with a line feed or carriage return character.
643 | $lch = $data[$strlen - 1];
644 | if ($lch != "\n" && $lch != "\r") {
645 | $data .= "\n";
646 | $strlen++;
647 | }
648 |
649 | // walk through each character
650 | for ($i = 0; $i < $strlen; $i++) {
651 | $ch = isset($data[$i]) ? $data[$i] : false;
652 | $nch = isset($data[$i + 1]) ? $data[$i + 1] : false;
653 |
654 | // open/close quotes, and inline quotes
655 | if ($ch == $this->enclosure) {
656 | if (!$enclosed) {
657 | if (ltrim($current, $white_spaces) == '') {
658 | $enclosed = true;
659 | $was_enclosed = true;
660 | } else {
661 | $this->error = 2;
662 | $error_row = count($rows) + 1;
663 | $error_col = $col + 1;
664 | $index = $error_row . '-' . $error_col;
665 | if (!isset($this->error_info[$index])) {
666 | $this->error_info[$index] = array(
667 | 'type' => 2,
668 | 'info' => 'Syntax error found on row ' . $error_row . '. Non-enclosed fields can not contain double-quotes.',
669 | 'row' => $error_row,
670 | 'field' => $error_col,
671 | 'field_name' => !empty($head[$col]) ? $head[$col] : null,
672 | );
673 | }
674 |
675 | $current .= $ch;
676 | }
677 | } elseif ($nch == $this->enclosure) {
678 | $current .= $ch;
679 | $i++;
680 | } elseif ($nch != $this->delimiter && $nch != "\r" && $nch != "\n") {
681 | $x = $i + 1;
682 | while (isset($data[$x]) && ltrim($data[$x], $white_spaces) == '') {
683 | $x++;
684 | }
685 | if ($data[$x] == $this->delimiter) {
686 | $enclosed = false;
687 | $i = $x;
688 | } else {
689 | if ($this->error < 1) {
690 | $this->error = 1;
691 | }
692 |
693 | $error_row = count($rows) + 1;
694 | $error_col = $col + 1;
695 | $index = $error_row . '-' . $error_col;
696 | if (!isset($this->error_info[$index])) {
697 | $this->error_info[$index] = array(
698 | 'type' => 1,
699 | 'info' =>
700 | 'Syntax error found on row ' . (count($rows) + 1) . '. ' .
701 | 'A single double-quote was found within an enclosed string. ' .
702 | 'Enclosed double-quotes must be escaped with a second double-quote.',
703 | 'row' => count($rows) + 1,
704 | 'field' => $col + 1,
705 | 'field_name' => !empty($head[$col]) ? $head[$col] : null,
706 | );
707 | }
708 |
709 | $current .= $ch;
710 | $enclosed = false;
711 | }
712 | } else {
713 | $enclosed = false;
714 | }
715 |
716 | // end of field/row/csv
717 | } elseif (($ch === $this->delimiter || $ch == "\n" || $ch == "\r" || $ch === false) && !$enclosed) {
718 | $key = !empty($head[$col]) ? $head[$col] : $col;
719 | $row[$key] = $was_enclosed ? $current : trim($current);
720 | $current = '';
721 | $was_enclosed = false;
722 | $col++;
723 |
724 | // end of row
725 | if (in_array($ch, ["\n", "\r", false], true)) {
726 | if ($this->_validate_offset($row_count) && $this->_validate_row_conditions($row, $this->conditions)) {
727 | if ($this->heading && empty($head)) {
728 | $head = $row;
729 | } elseif (empty($this->fields) || (!empty($this->fields) && (($this->heading && $row_count > 0) || !$this->heading))) {
730 | if (!empty($this->sort_by) && !empty($row[$this->sort_by])) {
731 | $sort_field = $row[$this->sort_by];
732 | if (isset($rows[$sort_field])) {
733 | $rows[$sort_field . '_0'] = &$rows[$sort_field];
734 | unset($rows[$sort_field]);
735 | $sn = 1;
736 | while (isset($rows[$sort_field . '_' . $sn])) {
737 | $sn++;
738 | }
739 | $rows[$sort_field . '_' . $sn] = $row;
740 | } else {
741 | $rows[$sort_field] = $row;
742 | }
743 |
744 | } else {
745 | $rows[] = $row;
746 | }
747 | }
748 | }
749 |
750 | $row = array();
751 | $col = 0;
752 | $row_count++;
753 |
754 | if ($this->sort_by === null && $this->limit !== null && count($rows) == $this->limit) {
755 | $i = $strlen;
756 | }
757 |
758 | if ($ch == "\r" && $nch == "\n") {
759 | $i++;
760 | }
761 | }
762 |
763 | // append character to current field
764 | } else {
765 | $current .= $ch;
766 | }
767 | }
768 |
769 | $this->titles = $head;
770 | if (!empty($this->sort_by)) {
771 | $sort_type = SortEnum::getSorting($this->sort_type);
772 | $this->sort_reverse ? krsort($rows, $sort_type) : ksort($rows, $sort_type);
773 |
774 | if ($this->offset !== null || $this->limit !== null) {
775 | $rows = array_slice($rows, ($this->offset === null ? 0 : $this->offset), $this->limit, true);
776 | }
777 | }
778 |
779 | if (!$this->keep_file_data) {
780 | $this->file_data = null;
781 | }
782 |
783 | return $rows;
784 | }
785 |
786 | /**
787 | * Create CSV data string from array
788 | *
789 | * @param array[] $data 2D array with data
790 | * @param array $fields field names
791 | * @param bool $append if true, field names will not be output
792 | * @param bool $is_php if a php die() call should be put on the
793 | * first line of the file, this is later
794 | * ignored when read.
795 | * @param string|null $delimiter field delimiter to use
796 | *
797 | * @return string CSV data
798 | */
799 | public function unparse($data = array(), $fields = array(), $append = FileProcessingModeEnum::MODE_FILE_OVERWRITE, $is_php = false, $delimiter = null) {
800 | if (!is_array($data) || empty($data)) {
801 | $data = &$this->data;
802 | } else {
803 | /** @noinspection ReferenceMismatchInspection */
804 | $this->data = $data;
805 | }
806 |
807 | if (!is_array($fields) || empty($fields)) {
808 | $fields = &$this->titles;
809 | }
810 |
811 | if ($delimiter === null) {
812 | $delimiter = $this->delimiter;
813 | }
814 |
815 | $string = $is_php ? "" . $this->linefeed : '';
816 | $entry = array();
817 |
818 | // create heading
819 | /** @noinspection ReferenceMismatchInspection */
820 | $fieldOrder = $this->_validate_fields_for_unparse($fields);
821 | if (!$fieldOrder && !empty($data)) {
822 | $column_count = count($data[0]);
823 | $columns = range(0, $column_count - 1, 1);
824 | $fieldOrder = array_combine($columns, $columns);
825 | }
826 |
827 | if ($this->heading && !$append && !empty($fields)) {
828 | foreach ($fieldOrder as $column_name) {
829 | $entry[] = $this->_enclose_value($column_name, $delimiter);
830 | }
831 |
832 | $string .= implode($delimiter, $entry) . $this->linefeed;
833 | $entry = array();
834 | }
835 |
836 | // create data
837 | foreach ($data as $key => $row) {
838 | foreach (array_keys($fieldOrder) as $index) {
839 | $cell_value = $row[$index];
840 | $entry[] = $this->_enclose_value($cell_value, $delimiter);
841 | }
842 |
843 | $string .= implode($delimiter, $entry) . $this->linefeed;
844 | $entry = array();
845 | }
846 |
847 | if ($this->convert_encoding) {
848 | /** @noinspection PhpComposerExtensionStubsInspection
849 | *
850 | * If you receive an error at the following 3 lines, you must enable
851 | * the following PHP extension:
852 | *
853 | * - if $use_mb_convert_encoding is true: mbstring
854 | * - if $use_mb_convert_encoding is false: iconv
855 | */
856 | $string = $this->use_mb_convert_encoding ?
857 | mb_convert_encoding($string, $this->output_encoding, $this->input_encoding) :
858 | iconv($this->input_encoding, $this->output_encoding, $string);
859 | }
860 |
861 | return $string;
862 | }
863 |
864 | private function _validate_fields_for_unparse($fields) {
865 | if (empty($fields)) {
866 | $fields = $this->titles;
867 | }
868 |
869 | if (empty($fields)) {
870 | return array();
871 | }
872 |
873 | // this is needed because sometime titles property is overwritten instead of using fields parameter!
874 | $titlesOnParse = !empty($this->data) ? array_keys(reset($this->data)) : array();
875 |
876 | // both are identical, also in ordering OR we have no data (only titles)
877 | if (empty($titlesOnParse) || array_values($fields) === array_values($titlesOnParse)) {
878 | return array_combine($fields, $fields);
879 | }
880 |
881 | // if renaming given by: $oldName => $newName (maybe with reorder and / or subset):
882 | // todo: this will only work if titles are unique
883 | $fieldOrder = array_intersect(array_flip($fields), $titlesOnParse);
884 | if (!empty($fieldOrder)) {
885 | return array_flip($fieldOrder);
886 | }
887 |
888 | $fieldOrder = array_intersect($fields, $titlesOnParse);
889 | if (!empty($fieldOrder)) {
890 | return array_combine($fieldOrder, $fieldOrder);
891 | }
892 |
893 | // original titles are not given in fields. that is okay if count is okay.
894 | if (count($fields) != count($titlesOnParse)) {
895 | throw new \UnexpectedValueException(
896 | "The specified fields do not match any titles and do not match column count.\n" .
897 | "\$fields was " . print_r($fields, true) .
898 | "\$titlesOnParse was " . print_r($titlesOnParse, true));
899 | }
900 |
901 | return array_combine($titlesOnParse, $fields);
902 | }
903 |
904 | /**
905 | * Load local file or string.
906 | *
907 | * Only use this function if auto() and parse() don't handle your data well.
908 | *
909 | * This function load_data() is able to handle BOMs and encodings. The data
910 | * is stored within the $this->file_data class field.
911 | *
912 | * @param string|null $input local CSV file or CSV data as a string
913 | *
914 | * @return bool True on success
915 | */
916 | public function load_data($input = null) {
917 | $data = null;
918 | $file = null;
919 |
920 | if (is_null($input)) {
921 | $file = $this->file;
922 | } elseif (\strlen($input) <= PHP_MAXPATHLEN && file_exists($input)) {
923 | $file = $input;
924 | } else {
925 | // It is CSV data as a string.
926 | $data = $input;
927 | }
928 |
929 | if (!empty($data) || $data = $this->_rfile($file)) {
930 | if ($this->file != $file) {
931 | $this->file = $file;
932 | }
933 |
934 | if (preg_match('/\.php$/i', $file) && preg_match('/<\?.*?\?>(.*)/ms', $data, $strip)) {
935 | $data = ltrim($strip[1]);
936 | }
937 |
938 | if (strpos($data, "\xef\xbb\xbf") === 0) {
939 | // strip off BOM (UTF-8)
940 | $data = substr($data, 3);
941 | $this->encoding('UTF-8');
942 | } elseif (strpos($data, "\xff\xfe") === 0) {
943 | // strip off BOM (UTF-16 little endian)
944 | $data = substr($data, 2);
945 | $this->encoding("UCS-2LE");
946 | } elseif (strpos($data, "\xfe\xff") === 0) {
947 | // strip off BOM (UTF-16 big endian)
948 | $data = substr($data, 2);
949 | $this->encoding("UTF-16");
950 | }
951 |
952 | if ($this->convert_encoding && $this->input_encoding !== $this->output_encoding) {
953 | /** @noinspection PhpComposerExtensionStubsInspection
954 | *
955 | * If you receive an error at the following 3 lines, you must enable
956 | * the following PHP extension:
957 | *
958 | * - if $use_mb_convert_encoding is true: mbstring
959 | * - if $use_mb_convert_encoding is false: iconv
960 | */
961 | $data = $this->use_mb_convert_encoding ?
962 | mb_convert_encoding($data, $this->output_encoding, $this->input_encoding) :
963 | iconv($this->input_encoding, $this->output_encoding, $data);
964 | }
965 |
966 | if (substr($data, -1) != "\n") {
967 | $data .= "\n";
968 | }
969 |
970 | $this->file_data = &$data;
971 | return true;
972 | }
973 |
974 | return false;
975 | }
976 |
977 | // ==============================================
978 | // ----- [ Internal Functions ] -----------------
979 | // ==============================================
980 |
981 | /**
982 | * Validate a row against specified conditions
983 | *
984 | * @param array $row array with values from a row
985 | * @param string|null $conditions specified conditions that the row must match
986 | *
987 | * @return true of false
988 | */
989 | protected function _validate_row_conditions($row = array(), $conditions = null) {
990 | if (!empty($row)) {
991 | if (!empty($conditions)) {
992 | $condition_array = (strpos($conditions, ' OR ') !== false) ?
993 | explode(' OR ', $conditions) :
994 | array($conditions);
995 | $or = '';
996 | foreach ($condition_array as $key => $value) {
997 | if (strpos($value, ' AND ') !== false) {
998 | $value = explode(' AND ', $value);
999 | $and = '';
1000 |
1001 | foreach ($value as $k => $v) {
1002 | $and .= $this->_validate_row_condition($row, $v);
1003 | }
1004 |
1005 | $or .= (strpos($and, '0') !== false) ? '0' : '1';
1006 | } else {
1007 | $or .= $this->_validate_row_condition($row, $value);
1008 | }
1009 | }
1010 |
1011 | return strpos($or, '1') !== false;
1012 | }
1013 |
1014 | return true;
1015 | }
1016 |
1017 | return false;
1018 | }
1019 |
1020 | /**
1021 | * Validate a row against a single condition
1022 | *
1023 | * @param array $row array with values from a row
1024 | * @param string $condition specified condition that the row must match
1025 | *
1026 | * @return string single 0 or 1
1027 | */
1028 | protected function _validate_row_condition($row, $condition) {
1029 | $operators = array(
1030 | '=',
1031 | 'equals',
1032 | 'is',
1033 | '!=',
1034 | 'is not',
1035 | '<',
1036 | 'is less than',
1037 | '>',
1038 | 'is greater than',
1039 | '<=',
1040 | 'is less than or equals',
1041 | '>=',
1042 | 'is greater than or equals',
1043 | 'contains',
1044 | 'does not contain',
1045 | );
1046 |
1047 | $operators_regex = array();
1048 |
1049 | foreach ($operators as $value) {
1050 | $operators_regex[] = preg_quote($value, '/');
1051 | }
1052 |
1053 | $operators_regex = implode('|', $operators_regex);
1054 |
1055 | if (preg_match('/^(.+) (' . $operators_regex . ') (.+)$/i', trim($condition), $capture)) {
1056 | $field = $capture[1];
1057 | $op = strtolower($capture[2]);
1058 | $value = $capture[3];
1059 | if ($op == 'equals' && preg_match('/^(.+) is (less|greater) than or$/i', $field, $m)) {
1060 | $field = $m[1];
1061 | $op = strtolower($m[2]) == 'less' ? '<=' : '>=';
1062 | }
1063 | if ($op == 'is' && preg_match('/^(less|greater) than (.+)$/i', $value, $m)) {
1064 | $value = $m[2];
1065 | $op = strtolower($m[1]) == 'less' ? '<' : '>';
1066 | }
1067 | if ($op == 'is' && preg_match('/^not (.+)$/i', $value, $m)) {
1068 | $value = $m[1];
1069 | $op = '!=';
1070 | }
1071 |
1072 | if (preg_match('/^([\'"])(.*)([\'"])$/', $value, $capture) && $capture[1] == $capture[3]) {
1073 | $value = strtr($capture[2], array(
1074 | "\\n" => "\n",
1075 | "\\r" => "\r",
1076 | "\\t" => "\t",
1077 | ));
1078 |
1079 | $value = stripslashes($value);
1080 | }
1081 |
1082 | if (array_key_exists($field, $row)) {
1083 | $op_equals = in_array($op, ['=', 'equals', 'is'], true);
1084 | if ($op_equals && $row[$field] == $value) {
1085 | return '1';
1086 | } elseif (($op == '!=' || $op == 'is not') && $row[$field] != $value) {
1087 | return '1';
1088 | } elseif (($op == '<' || $op == 'is less than') && $row[$field] < $value) {
1089 | return '1';
1090 | } elseif (($op == '>' || $op == 'is greater than') && $row[$field] > $value) {
1091 | return '1';
1092 | } elseif (($op == '<=' || $op == 'is less than or equals') && $row[$field] <= $value) {
1093 | return '1';
1094 | } elseif (($op == '>=' || $op == 'is greater than or equals') && $row[$field] >= $value) {
1095 | return '1';
1096 | } elseif ($op == 'contains' && preg_match('/' . preg_quote($value, '/') . '/i', $row[$field])) {
1097 | return '1';
1098 | } elseif ($op == 'does not contain' && !preg_match('/' . preg_quote($value, '/') . '/i', $row[$field])) {
1099 | return '1';
1100 | } else {
1101 | return '0';
1102 | }
1103 | }
1104 | }
1105 |
1106 | return '1';
1107 | }
1108 |
1109 | /**
1110 | * Validates if the row is within the offset or not if sorting is disabled
1111 | *
1112 | * @param int $current_row the current row number being processed
1113 | *
1114 | * @return true of false
1115 | */
1116 | protected function _validate_offset($current_row) {
1117 | return
1118 | $this->sort_by !== null ||
1119 | $this->offset === null ||
1120 | $current_row >= $this->offset ||
1121 | ($this->heading && $current_row == 0);
1122 | }
1123 |
1124 | /**
1125 | * Enclose values if needed
1126 | * - only used by unparse()
1127 | *
1128 | * @param string|null $value Cell value to process
1129 | * @param string $delimiter Character to put between cells on the same row
1130 | *
1131 | * @return string Processed value
1132 | */
1133 | protected function _enclose_value($value, $delimiter) {
1134 | if ($value !== null && $value != '') {
1135 | $delimiter_quoted = $delimiter ?
1136 | preg_quote($delimiter, '/') . "|"
1137 | : '';
1138 | $enclosure_quoted = preg_quote($this->enclosure, '/');
1139 | $pattern = "/" . $delimiter_quoted . $enclosure_quoted . "|\n|\r/i";
1140 | if ($this->enclose_all || preg_match($pattern, $value) || strpos($value, ' ') === 0 || substr($value, -1) == ' ') {
1141 | $value = str_replace($this->enclosure, $this->enclosure . $this->enclosure, $value);
1142 | $value = $this->enclosure . $value . $this->enclosure;
1143 | }
1144 | }
1145 |
1146 | return $value;
1147 | }
1148 |
1149 | /**
1150 | * Check file data
1151 | *
1152 | * @param string|null $file local filename
1153 | *
1154 | * @return bool
1155 | */
1156 | protected function _check_data($file = null) {
1157 | if (empty($this->file_data)) {
1158 | if (is_null($file)) {
1159 | $file = $this->file;
1160 | }
1161 |
1162 | return $this->load_data($file);
1163 | }
1164 |
1165 | return true;
1166 | }
1167 |
1168 | /**
1169 | * Check if passed info might be delimiter
1170 | * Only used by find_delimiter
1171 | *
1172 | * @param string $char Potential field separating character
1173 | * @param array $array Frequency
1174 | * @param int $depth Number of analyzed rows
1175 | * @param string $preferred Preferred delimiter characters
1176 | *
1177 | * @return string|false special string used for delimiter selection, or false
1178 | */
1179 | protected function _check_count($char, $array, $depth, $preferred) {
1180 | if ($depth === count($array)) {
1181 | $first = null;
1182 | $equal = null;
1183 | $almost = false;
1184 | foreach ($array as $key => $value) {
1185 | if ($first == null) {
1186 | $first = $value;
1187 | } elseif ($value == $first && $equal !== false) {
1188 | $equal = true;
1189 | } elseif ($value == $first + 1 && $equal !== false) {
1190 | $equal = true;
1191 | $almost = true;
1192 | } else {
1193 | $equal = false;
1194 | }
1195 | }
1196 |
1197 | if ($equal) {
1198 | $match = $almost ? 2 : 1;
1199 | $pref = strpos($preferred, $char);
1200 | $pref = ($pref !== false) ? str_pad($pref, 3, '0', STR_PAD_LEFT) : '999';
1201 |
1202 | return $pref . $match . '.' . (99999 - str_pad($first, 5, '0', STR_PAD_LEFT));
1203 | } else {
1204 | return false;
1205 | }
1206 | }
1207 | return false;
1208 | }
1209 |
1210 | /**
1211 | * Read local file.
1212 | *
1213 | * @param string $file local filename
1214 | *
1215 | * @return string|false Data from file, or false on failure
1216 | */
1217 | protected function _rfile($file) {
1218 | if (is_readable($file)) {
1219 | $data = file_get_contents($file);
1220 | if ($data === false) {
1221 | return false;
1222 | }
1223 | return rtrim($data, "\r\n");
1224 | }
1225 |
1226 | return false;
1227 | }
1228 |
1229 | /**
1230 | * Write to local file
1231 | *
1232 | * @param string $file local filename
1233 | * @param string $content data to write to file
1234 | * @param string $mode fopen() mode
1235 | * @param int $lock flock() mode
1236 | *
1237 | * @return bool
1238 | * True on success
1239 | *
1240 | */
1241 | protected function _wfile($file, $content = '', $mode = 'wb', $lock = LOCK_EX) {
1242 | if ($fp = fopen($file, $mode)) {
1243 | flock($fp, $lock);
1244 | $re = fwrite($fp, $content);
1245 | $re2 = fclose($fp);
1246 |
1247 | if ($re !== false && $re2 !== false) {
1248 | return true;
1249 | }
1250 | }
1251 |
1252 | return false;
1253 | }
1254 |
1255 | /**
1256 | * Detect separator using a nonstandard hack: such file starts with the
1257 | * first line containing only "sep=;", where the last character is the
1258 | * separator. Microsoft Excel is able to open such files.
1259 | *
1260 | * @param string $data file data
1261 | *
1262 | * @return string|false detected delimiter, or false if none found
1263 | */
1264 | protected function _get_delimiter_from_sep_row($data) {
1265 | $sep = false;
1266 | // 32 bytes should be quite enough data for our sniffing, chosen arbitrarily
1267 | $sepPrefix = substr($data, 0, 32);
1268 | if (preg_match('/^sep=(.)\\r?\\n/i', $sepPrefix, $sepMatch)) {
1269 | // we get separator.
1270 | $sep = $sepMatch[1];
1271 | }
1272 | return $sep;
1273 | }
1274 |
1275 | /**
1276 | * Support for Excel-compatible sep=? row.
1277 | *
1278 | * @param string $data_string file data to be updated
1279 | *
1280 | * @return bool TRUE if sep= line was found at the very beginning of the file
1281 | */
1282 | protected function _detect_and_remove_sep_row_from_data(&$data_string) {
1283 | $sep = $this->_get_delimiter_from_sep_row($data_string);
1284 | if ($sep === false) {
1285 | return false;
1286 | }
1287 |
1288 | $this->delimiter = $sep;
1289 |
1290 | // likely to be 5, but let's not assume we're always single-byte.
1291 | $pos = 4 + strlen($sep);
1292 | // the next characters should be a line-end
1293 | if (substr($data_string, $pos, 1) === "\r") {
1294 | $pos++;
1295 | }
1296 | if (substr($data_string, $pos, 1) === "\n") {
1297 | $pos++;
1298 | }
1299 |
1300 | // remove delimiter and its line-end (the data param is by-ref!)
1301 | /** @noinspection CallableParameterUseCaseInTypeContextInspection */
1302 | $data_string = substr($data_string, $pos);
1303 | return true;
1304 | }
1305 |
1306 | /**
1307 | * @param int $search_depth Number of rows to analyze
1308 | * @param string $preferred Preferred delimiter characters
1309 | * @param string $enclosure Enclosure character, default is double quote
1310 | * @param string $data The file content
1311 | */
1312 | protected function _guess_delimiter($search_depth, $preferred, $enclosure, &$data) {
1313 | $chars = [];
1314 | $strlen = strlen($data);
1315 | $enclosed = false;
1316 | $current_row = 1;
1317 | $to_end = true;
1318 |
1319 | // The dash is the only character we don't want quoted, as it would
1320 | // prevent character ranges within $auto_non_chars:
1321 | $quoted_auto_non_chars = preg_quote($this->auto_non_chars, '/');
1322 | $quoted_auto_non_chars = str_replace('\-', '-', $quoted_auto_non_chars);
1323 | $pattern = '/[' . $quoted_auto_non_chars . ']/i';
1324 |
1325 | // walk specific depth finding possible delimiter characters
1326 | for ($i = 0; $i < $strlen; $i++) {
1327 | $ch = $data[$i];
1328 | $nch = isset($data[$i + 1]) ? $data[$i + 1] : false;
1329 | $pch = isset($data[$i - 1]) ? $data[$i - 1] : false;
1330 |
1331 | // open and closing quotes
1332 | $is_newline = ($ch == "\n" && $pch != "\r") || $ch == "\r";
1333 | if ($ch == $enclosure) {
1334 | if (!$enclosed || $nch != $enclosure) {
1335 | $enclosed = $enclosed ? false : true;
1336 | } elseif ($enclosed) {
1337 | $i++;
1338 | }
1339 |
1340 | // end of row
1341 | } elseif ($is_newline && !$enclosed) {
1342 | if ($current_row >= $search_depth) {
1343 | $strlen = 0;
1344 | $to_end = false;
1345 | } else {
1346 | $current_row++;
1347 | }
1348 |
1349 | // count character
1350 | } elseif (!$enclosed) {
1351 | if (!preg_match($pattern, $ch)) {
1352 | if (!isset($chars[$ch][$current_row])) {
1353 | $chars[$ch][$current_row] = 1;
1354 | } else {
1355 | $chars[$ch][$current_row]++;
1356 | }
1357 | }
1358 | }
1359 | }
1360 |
1361 | // filtering
1362 | $depth = $to_end ? $current_row - 1 : $current_row;
1363 | $filtered = [];
1364 | foreach ($chars as $char => $value) {
1365 | if ($match = $this->_check_count($char, $value, $depth, $preferred)) {
1366 | $filtered[$match] = $char;
1367 | }
1368 | }
1369 |
1370 | // capture most probable delimiter
1371 | ksort($filtered);
1372 | $this->delimiter = reset($filtered);
1373 | }
1374 |
1375 | /**
1376 | * getCollection
1377 | * Returns a Illuminate/Collection object
1378 | * This may prove to be helpful to people who want to
1379 | * create macros, and or use map functions
1380 | *
1381 | * @access public
1382 | * @link https://laravel.com/docs/5.6/collections
1383 | *
1384 | * @throws \ErrorException - If the Illuminate\Support\Collection class is not found
1385 | *
1386 | * @return Collection
1387 | */
1388 | public function getCollection() {
1389 | //does the Illuminate\Support\Collection class exists?
1390 | //this uses the autoloader to try to determine
1391 | //@see http://php.net/manual/en/function.class-exists.php
1392 | if (class_exists('Illuminate\Support\Collection', true) == false) {
1393 | throw new \ErrorException('It would appear you have not installed the illuminate/support package!');
1394 | }
1395 |
1396 | //return the collection
1397 | return new Collection($this->data);
1398 | }
1399 | }
1400 |
--------------------------------------------------------------------------------
/parsecsv-for-php/src/enums/AbstractEnum.php:
--------------------------------------------------------------------------------
1 | isValid($value)) {
16 | throw new \UnexpectedValueException("Value '$value' is not part of the enum " . get_called_class());
17 | }
18 | $this->value = $value;
19 | }
20 |
21 | public static function getConstants() {
22 | $class = get_called_class();
23 | $reflection = new \ReflectionClass($class);
24 |
25 | return $reflection->getConstants();
26 | }
27 |
28 | /**
29 | * Check if enum value is valid
30 | *
31 | * @param $value
32 | *
33 | * @return bool
34 | */
35 | public static function isValid($value) {
36 | return in_array($value, static::getConstants(), true);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/parsecsv-for-php/src/enums/DatatypeEnum.php:
--------------------------------------------------------------------------------
1 | null,
44 | self::TYPE_INT => 'isValidInteger',
45 | self::TYPE_BOOL => 'isValidBoolean',
46 | self::TYPE_FLOAT => 'isValidFloat',
47 | self::TYPE_DATE => 'isValidDate',
48 | );
49 |
50 | /**
51 | * Checks data type for given string.
52 | *
53 | * @param string $value
54 | *
55 | * @return bool|string
56 | */
57 | public static function getValidTypeFromSample($value) {
58 | $value = trim((string) $value);
59 |
60 | if (empty($value)) {
61 | return false;
62 | }
63 |
64 | foreach (self::$validators as $type => $validator) {
65 | if ($validator === null) {
66 | continue;
67 | }
68 |
69 | if (method_exists(__CLASS__, $validator) && self::$validator($value)) {
70 | return $type;
71 | }
72 | }
73 |
74 | return self::__DEFAULT;
75 | }
76 |
77 | /**
78 | * Check if string is float value.
79 | *
80 | * @param string $value
81 | *
82 | * @return bool
83 | */
84 | private static function isValidFloat($value) {
85 | return (bool) preg_match(self::REGEX_FLOAT, $value);
86 | }
87 |
88 | /**
89 | * Check if string is integer value.
90 | *
91 | * @param string $value
92 | *
93 | * @return bool
94 | */
95 | private static function isValidInteger($value) {
96 | return (bool) preg_match(self::REGEX_INT, $value);
97 | }
98 |
99 | /**
100 | * Check if string is boolean.
101 | *
102 | * @param string $value
103 | *
104 | * @return bool
105 | */
106 | private static function isValidBoolean($value) {
107 | return (bool) preg_match(self::REGEX_BOOL, $value);
108 | }
109 |
110 | /**
111 | * Check if string is date.
112 | *
113 | * @param string $value
114 | *
115 | * @return bool
116 | */
117 | private static function isValidDate($value) {
118 | return (bool) strtotime($value);
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/parsecsv-for-php/src/enums/FileProcessingModeEnum.php:
--------------------------------------------------------------------------------
1 | SORT_REGULAR,
18 | self::SORT_TYPE_STRING => SORT_STRING,
19 | self::SORT_TYPE_NUMERIC => SORT_NUMERIC,
20 | );
21 |
22 | public static function getSorting($type) {
23 | if (array_key_exists($type, self::$sorting)) {
24 | return self::$sorting[$type];
25 | }
26 |
27 | return self::$sorting[self::__DEFAULT];
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/parsecsv-for-php/src/extensions/DatatypeTrait.php:
--------------------------------------------------------------------------------
1 | = 5.5
47 | *
48 | * @uses DatatypeEnum::getValidTypeFromSample
49 | *
50 | * @return array|bool
51 | */
52 | public function getDatatypes() {
53 | if (empty($this->data)) {
54 | $this->data = $this->_parse_string();
55 | }
56 | if (!is_array($this->data)) {
57 | throw new \UnexpectedValueException('No data set yet.');
58 | }
59 |
60 | $result = [];
61 | foreach ($this->titles as $cName) {
62 | $column = array_column($this->data, $cName);
63 | $cDatatypes = array_map(DatatypeEnum::class . '::getValidTypeFromSample', $column);
64 |
65 | $result[$cName] = $this->getMostFrequentDatatypeForColumn($cDatatypes);
66 | }
67 |
68 | $this->data_types = $result;
69 |
70 | return !empty($this->data_types) ? $this->data_types : [];
71 | }
72 |
73 | /**
74 | * Check data type of titles / first row for auto detecting if this could be
75 | * a heading line.
76 | *
77 | * Requires PHP >= 5.5
78 | *
79 | * @uses DatatypeEnum::getValidTypeFromSample
80 | *
81 | * @return bool
82 | */
83 | public function autoDetectFileHasHeading() {
84 | if (empty($this->data)) {
85 | throw new \UnexpectedValueException('No data set yet.');
86 | }
87 |
88 | if ($this->heading) {
89 | $firstRow = $this->titles;
90 | } else {
91 | $firstRow = $this->data[0];
92 | }
93 |
94 | $firstRow = array_filter($firstRow);
95 | if (empty($firstRow)) {
96 | return false;
97 | }
98 |
99 | $firstRowDatatype = array_map(DatatypeEnum::class . '::getValidTypeFromSample', $firstRow);
100 |
101 | return $this->getMostFrequentDatatypeForColumn($firstRowDatatype) === DatatypeEnum::TYPE_STRING;
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/parsecsv-for-php/tests/Bootstrap.php:
--------------------------------------------------------------------------------
1 | csv = new Csv(null, $offset);
18 | $this->assertTrue(is_numeric($this->csv->offset));
19 | $this->assertEquals($offset, $this->csv->offset);
20 | }
21 |
22 | public function test_limit_param() {
23 | $limit = 10;
24 | $this->csv = new Csv(null, null, $limit);
25 | $this->assertTrue(is_numeric($this->csv->limit));
26 | $this->assertEquals($limit, $this->csv->limit);
27 | }
28 |
29 | public function test_conditions_param() {
30 | $conditions = 'some column NOT value';
31 | $this->csv = new Csv(null, null, null, $conditions);
32 | $this->assertTrue(is_string($this->csv->conditions));
33 | $this->assertEquals($conditions, $this->csv->conditions);
34 | }
35 |
36 | public function test_keep_file_data_param() {
37 | $keep = true;
38 | $this->csv = new Csv(null, null, null, null, $keep);
39 | $this->assertTrue(is_bool($this->csv->keep_file_data));
40 | $this->assertEquals($keep, $this->csv->keep_file_data);
41 | }
42 |
43 | public function test_input_param() {
44 | $csv = "col1,col2,col3\r\nval1,val2,val3\r\nval1A,val2A,val3A\r\n";
45 | $this->csv = new Csv($csv, null, null, null, true);
46 | $this->assertTrue(is_string($this->csv->file_data));
47 | $this->assertEquals($csv, $this->csv->file_data);
48 | }
49 |
50 | /**
51 | * @runInSeparateProcess because download.php uses header()
52 | *
53 | * @see https://github.com/sebastianbergmann/phpunit/issues/720#issuecomment-10421092
54 | */
55 | public function testCodeExamples() {
56 | chdir('examples');
57 | foreach (glob('*.php') as $script_file) {
58 | ob_start();
59 | /** @noinspection PhpIncludeInspection */
60 | require $script_file;
61 | $ob_get_clean = ob_get_clean();
62 | $verb = strtok($script_file, '_.');
63 |
64 | if (!in_array($verb, ['download', 'save'], true)) {
65 | $this->assertContains('