1: <?php
2: /**
3: * DataTables PHP libraries.
4: *
5: * PHP libraries for DataTables and DataTables Editor, utilising PHP 5.3+.
6: *
7: * @author SpryMedia
8: * @copyright 2012 SpryMedia ( http://sprymedia.co.uk )
9: * @license http://editor.datatables.net/license DataTables Editor
10: * @link http://editor.datatables.net
11: */
12:
13: namespace DataTables\Editor;
14: if (!defined('DATATABLES')) exit();
15:
16: use
17: DataTables,
18: DataTables\Editor,
19: DataTables\Editor\Options,
20: DataTables\Editor\Join;
21:
22:
23: /**
24: * Field definitions for the DataTables Editor.
25: *
26: * Each Database column that is used with Editor can be described with this
27: * Field method (both for Editor and Join instances). It basically tells
28: * Editor what table column to use, how to format the data and if you want
29: * to read and/or write this column.
30: *
31: * Field instances are used with the {@link Editor::field} and
32: * {@link Join::field} methods to describe what fields should be interacted
33: * with by the editable table.
34: *
35: * @example
36: * Simply get a column with the name "city". No validation is performed.
37: * <code>
38: * Field::inst( 'city' )
39: * </code>
40: *
41: * @example
42: * Get a column with the name "first_name" - when edited a value must
43: * be given due to the "required" validation from the {@link Validate} class.
44: * <code>
45: * Field::inst( 'first_name' )->validator( 'Validate::required' )
46: * </code>
47: *
48: * @example
49: * Working with a date field, which is validated, and also has *get* and
50: * *set* formatters.
51: * <code>
52: * Field::inst( 'registered_date' )
53: * ->validator( 'Validate::dateFormat', 'D, d M y' )
54: * ->getFormatter( 'Format::date_sql_to_format', 'D, d M y' )
55: * ->setFormatter( 'Format::date_format_to_sql', 'D, d M y' )
56: * </code>
57: *
58: * @example
59: * Using an alias in the first parameter
60: * <code>
61: * Field::inst( 'name.first as first_name' )
62: * </code>
63: */
64: class Field extends DataTables\Ext {
65: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
66: * Statics
67: */
68:
69: /** Set option flag (`set()`) - do not set data */
70: const SET_NONE = 'none';
71:
72: /** Set option flag (`set()`) - write to database on both create and edit */
73: const SET_BOTH = 'both';
74:
75: /** Set option flag (`set()`) - write to database only on create */
76: const SET_CREATE = 'create';
77:
78: /** Set option flag (`set()`) - write to database only on edit */
79: const SET_EDIT = 'edit';
80:
81:
82: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
83: * Constructor
84: */
85:
86: /**
87: * Field instance constructor.
88: * @param string $dbField Name of the database column
89: * @param string $name Name to use in the JSON output from Editor and the
90: * HTTP submit from the client-side when editing. If not given then the
91: * $dbField name is used.
92: */
93: function __construct( $dbField=null, $name=null )
94: {
95: if ( $dbField !== null && $name === null ) {
96: // Allow just a single parameter to be passed - each can be
97: // overridden if needed later using the API.
98: $this->name( $dbField );
99: $this->dbField( $dbField );
100: }
101: else {
102: $this->name( $name );
103: $this->dbField( $dbField );
104: }
105: }
106:
107:
108:
109: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
110: * Private parameters
111: */
112:
113: /** @var string */
114: private $_dbField = null;
115:
116: /** @var boolean */
117: private $_get = true;
118:
119: /** @var mixed */
120: private $_getFormatter = null;
121:
122: /** @var mixed */
123: private $_getFormatterOpts = null;
124:
125: /** @var mixed */
126: private $_getValue = null;
127:
128: /** @var Options */
129: private $_opts = null;
130:
131: /** @var SearchPaneOptions */
132: private $_spopts = null;
133:
134: /** @var callable */
135: private $_optsFn = null;
136:
137: /** @var callable */
138: private $_spoptsFn = null;
139:
140: /** @var string */
141: private $_name = null;
142:
143: /** @var string */
144: private $_set = Field::SET_BOTH;
145:
146: /** @var mixed */
147: private $_setFormatter = null;
148:
149: /** @var mixed */
150: private $_setFormatterOpts = null;
151:
152: /** @var mixed */
153: private $_setValue = null;
154:
155: /** @var mixed */
156: private $_validator = array();
157:
158: /** @var Upload */
159: private $_upload = null;
160:
161: /** @var callable */
162: private $_xss = null;
163:
164: /** @var boolean */
165: private $_xssFormat = true;
166:
167:
168:
169: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
170: * Public methods
171: */
172:
173:
174: /**
175: * Get / set the DB field name.
176: *
177: * Note that when used as a setter, an alias can be given for the field
178: * using the SQL `as` keyword - for example: `firstName as name`. In this
179: * situation the dbField is set to the field name before the `as`, and the
180: * field's name (`name()`) is set to the name after the ` as `.
181: *
182: * As a result of this, the following constructs have identical
183: * functionality:
184: *
185: * Field::inst( 'firstName as name' );
186: * Field::inst( 'firstName', 'name' );
187: *
188: * @param string $_ Value to set if using as a setter.
189: * @return string|self The name of the db field if no parameter is given,
190: * or self if used as a setter.
191: */
192: public function dbField ( $_=null )
193: {
194: if ( $_ === null ) {
195: return $this->_dbField;
196: }
197:
198: // Don't split on an `as` inside paraenthesis
199: $a = preg_split( '/ as (?![^\(]*\))/i', $_ );
200: if ( count($a) > 1 ) {
201: $this->_dbField = trim( $a[0] );
202: $this->_name = trim( $a[1] );
203: }
204: else {
205: $this->_dbField = $_;
206: }
207:
208: return $this;
209: }
210:
211:
212: /**
213: * Get / set the 'get' property of the field.
214: *
215: * A field can be marked as write only when setting the get property to false
216: * here.
217: * @param boolean $_ Value to set if using as a setter.
218: * @return boolean|self The get property if no parameter is given, or self
219: * if used as a setter.
220: */
221: public function get ( $_=null )
222: {
223: return $this->_getSet( $this->_get, $_ );
224: }
225:
226:
227: /**
228: * Get formatter for the field's data.
229: *
230: * When the data has been retrieved from the server, it can be passed through
231: * a formatter here, which will manipulate (format) the data as required. This
232: * can be useful when, for example, working with dates and a particular format
233: * is required on the client-side.
234: *
235: * Editor has a number of formatters available with the {@link Format} class
236: * which can be used directly with this method.
237: * @param callable|string $_ Value to set if using as a setter. Can be given as
238: * a closure function or a string with a reference to a function that will
239: * be called with call_user_func().
240: * @param mixed $opts Variable that is passed through to the get formatting
241: * function - can be useful for passing through extra information such as
242: * date formatting string, or a required flag. The actual options available
243: * depend upon the formatter used.
244: * @return callable|string|self The get formatter if no parameter is given, or
245: * self if used as a setter.
246: */
247: public function getFormatter ( $_=null, $opts=null )
248: {
249: if ( $opts !== null ) {
250: $this->_getFormatterOpts = $opts;
251: }
252: return $this->_getSet( $this->_getFormatter, $_ );
253: }
254:
255:
256: /**
257: * Get / set a get value. If given, then this value is used to send to the
258: * client-side, regardless of what value is held by the database.
259: *
260: * @param callable|string|number $_ Value to set, or no value to use as a
261: * getter
262: * @return callable|string|self Value if used as a getter, or self if used
263: * as a setter.
264: */
265: public function getValue ( $_=null )
266: {
267: return $this->_getSet( $this->_getValue, $_ );
268: }
269:
270:
271: /**
272: * Get / set the 'name' property of the field.
273: *
274: * The name is typically the same as the dbField name, since it makes things
275: * less confusing(!), but it is possible to set a different name for the data
276: * which is used in the JSON returned to DataTables in a 'get' operation and
277: * the field name used in a 'set' operation.
278: * @param string $_ Value to set if using as a setter.
279: * @return string|self The name property if no parameter is given, or self
280: * if used as a setter.
281: */
282: public function name ( $_=null )
283: {
284: return $this->_getSet( $this->_name, $_ );
285: }
286:
287:
288: /**
289: * Get a list of values that can be used for the options list in radio,
290: * select and checkbox inputs from the database for this field.
291: *
292: * Note that this is for simple 'label / value' pairs only. For more complex
293: * data, including pairs that require joins and where conditions, use a
294: * closure to provide a query
295: *
296: * @param string|callable $table Database table name to use to get the
297: * paired data from, or a closure function if providing a method
298: * @param string $value Table column name that contains the pair's
299: * value. Not used if the first parameter is given as a closure
300: * @param string $label Table column name that contains the pair's
301: * label. Not used if the first parameter is given as a closure
302: * @param callable $condition Function that will add `where`
303: * conditions to the query
304: * @param callable $format Function will render each label
305: * @param string $order SQL ordering
306: * @return Field Self for chaining
307: */
308: public function options ( $table=null, $value=null, $label=null, $condition=null, $format=null, $order=null )
309: {
310: if ( $table === null ) {
311: return $this->_opts;
312: }
313:
314: // Overloads for backwards compatibility
315: if ( is_a( $table, '\DataTables\Editor\Options' ) ) {
316: // Options class
317: $this->_optsFn = null;
318: $this->_opts = $table;
319: }
320: else if ( is_callable($table) && is_object($table) ) {
321: // Function
322: $this->_opts = null;
323: $this->_optsFn = $table;
324: }
325: else {
326: $this->_optsFn = null;
327: $this->_opts = Options::inst()
328: ->table( $table )
329: ->value( $value )
330: ->label( $label );
331:
332: if ( $condition ) {
333: $this->_opts->where( $condition );
334: }
335:
336: if ( $format ) {
337: $this->_opts->render( $format );
338: }
339:
340: if ( $order ) {
341: $this->_opts->order( $order );
342: }
343: }
344:
345: return $this;
346: }
347:
348: /**
349: * Get a list of values that can be used for the options list in SearchPanes
350: *
351: * @param SearchPaneOptions|callable $spInput SearchPaneOptions instance or a closure function if providing a method
352: * @return self
353: */
354: public function searchPaneOptions ( $spInput=null )
355: {
356: if ( $spInput === null ) {
357: return $this->_spopts;
358: }
359:
360: // Overloads for backwards compatibility
361: if ( is_a( $spInput, '\DataTables\Editor\SearchPaneOptions' ) ) {
362: // Options class
363: $this->_spoptsFn = null;
364: $this->_spopts = $spInput;
365: }
366: else if ( is_callable($spInput) && is_object($spInput) ) {
367: // Function
368: $this->_spopts = null;
369: $this->_spoptsFn = $spInput;
370: }
371:
372: return $this;
373: }
374:
375:
376:
377: /**
378: * Get / set the 'set' property of the field.
379: *
380: * A field can be marked as read only using this option, to be set only
381: * during an create or edit action or to be set during both actions. This
382: * provides the ability to have fields that are only set when a new row is
383: * created (for example a "created" time stamp).
384: * @param string|boolean $_ Value to set when the method is being used as a
385: * setter (leave as undefined to use as a getter). This can take the
386: * value of:
387: *
388: * * `true` - Same as `Field::SET_BOTH`
389: * * `false` - Same as `Field::SET_NONE`
390: * * `Field::SET_BOTH` - Set the database value on both create and edit commands
391: * * `Field::SET_NONE` - Never set the database value
392: * * `Field::SET_CREATE` - Set the database value only on create
393: * * `Field::SET_EDIT` - Set the database value only on edit
394: * @return string|self The set property if no parameter is given, or self
395: * if used as a setter.
396: */
397: public function set ( $_=null )
398: {
399: if ( $_ === true ) {
400: $_ = Field::SET_BOTH;
401: }
402: else if ( $_ === false ) {
403: $_ = Field::SET_NONE;
404: }
405:
406: return $this->_getSet( $this->_set, $_ );
407: }
408:
409:
410: /**
411: * Set formatter for the field's data.
412: *
413: * When the data has been retrieved from the server, it can be passed through
414: * a formatter here, which will manipulate (format) the data as required. This
415: * can be useful when, for example, working with dates and a particular format
416: * is required on the client-side.
417: *
418: * Editor has a number of formatters available with the {@link Format} class
419: * which can be used directly with this method.
420: * @param callable|string $_ Value to set if using as a setter. Can be given as
421: * a closure function or a string with a reference to a function that will
422: * be called with call_user_func().
423: * @param mixed $opts Variable that is passed through to the get formatting
424: * function - can be useful for passing through extra information such as
425: * date formatting string, or a required flag. The actual options available
426: * depend upon the formatter used.
427: * @return callable|string|self The set formatter if no parameter is given, or
428: * self if used as a setter.
429: */
430: public function setFormatter ( $_=null, $opts=null )
431: {
432: if ( $opts !== null ) {
433: $this->_setFormatterOpts = $opts;
434: }
435: return $this->_getSet( $this->_setFormatter, $_ );
436: }
437:
438:
439: /**
440: * Get / set a set value. If given, then this value is used to write to the
441: * database regardless of what data is sent from the client-side.
442: *
443: * @param callable|string|number $_ Value to set, or no value to use as a
444: * getter
445: * @return callable|string|self Value if used as a getter, or self if used
446: * as a setter.
447: */
448: public function setValue ( $_=null )
449: {
450: return $this->_getSet( $this->_setValue, $_ );
451: }
452:
453:
454: /**
455: * Get / set the upload class for this field.
456: * @param Upload $_ Upload class if used as a setter
457: * @return Upload|self Value if used as a getter, or self if used
458: * as a setter.
459: */
460: public function upload ( $_=null )
461: {
462: return $this->_getSet( $this->_upload, $_ );
463: }
464:
465:
466: /**
467: * Get / set the 'validator' of the field.
468: *
469: * The validator can be used to check if any abstract piece of data is valid
470: * or not according to the given rules of the validation function used.
471: *
472: * Multiple validation options can be applied to a field instance by calling
473: * this method multiple times. For example, it would be possible to have a
474: * 'required' validation and a 'maxLength' validation with multiple calls.
475: *
476: * Editor has a number of validation available with the {@link Validate} class
477: * which can be used directly with this method.
478: * @param callable|string $_ Value to set if using as the validation method.
479: * Can be given as a closure function or a string with a reference to a
480: * function that will be called with call_user_func().
481: * @param mixed $opts Variable that is passed through to the validation
482: * function - can be useful for passing through extra information such as
483: * date formatting string, or a required flag. The actual options available
484: * depend upon the validation function used.
485: * @return callable|string|self The validation method if no parameter is given,
486: * or self if used as a setter.
487: */
488: public function validator ( $_=null, $opts=null )
489: {
490: if ( $_ === null ) {
491: return $this->_validator;
492: }
493: else {
494: $this->_validator[] = array(
495: "func" => $_,
496: "opts" => $opts
497: );
498: }
499:
500: return $this;
501: }
502:
503:
504: /**
505: * Set a formatting method that will be used for XSS checking / removal.
506: * This should be a function that takes a single argument (the value to be
507: * cleaned) and returns the cleaned value.
508: *
509: * Editor will use HtmLawed by default for this operation, which is built
510: * into the software and no additional configuration is required, but a
511: * custom function can be used if you wish to use a different formatter such
512: * as HTMLPurifier.
513: *
514: * If you wish to disable this option (which you would only do if you are
515: * absolutely confident that your validation will pick up on any XSS inputs)
516: * simply provide a closure function that returns the value given to the
517: * function. This is _not_ recommended.
518: *
519: * @param callable|false $xssFormatter XSS cleaner function, use `false` or
520: * `null` to disable XSS cleaning.
521: * @return Field Self for chaining.
522: */
523: public function xss ( $xssFormatter )
524: {
525: if ( $xssFormatter === true || $xssFormatter === false || $xssFormatter === null ) {
526: $this->_xssFormat = $xssFormatter;
527: }
528: else {
529: $this->_xss = $xssFormatter;
530: }
531:
532: return $this;
533: }
534:
535:
536:
537: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
538: * Internal methods
539: * Used by the Editor class and not generally for public use
540: */
541:
542: /**
543: * Check to see if a field should be used for a particular action (get or set).
544: *
545: * Called by the Editor / Join class instances - not expected for general
546: * consumption - internal.
547: * @param string $action Direction that the data is travelling - 'get' is
548: * reading DB data, `create` and `edit` for writing to the DB
549: * @param array $data Data submitted from the client-side when setting.
550: * @return boolean true if the field should be used in the get / set.
551: * @internal
552: */
553: public function apply ( $action, $data=null )
554: {
555: if ( $action === 'get' ) {
556: // Get action - can we get this field
557: return $this->_get;
558: }
559: else {
560: // Note that validation must be done on input data before we get here
561:
562: // Create or edit action, are we configured to use this field
563: if ( $action === 'create' &&
564: ($this->_set === Field::SET_NONE || $this->_set === Field::SET_EDIT)
565: ) {
566: return false;
567: }
568: else if ( $action === 'edit' &&
569: ($this->_set === Field::SET_NONE || $this->_set === Field::SET_CREATE)
570: ) {
571: return false;
572: }
573:
574: // Check it was in the submitted data. If not, then not required
575: // (validation would have failed if it was) and therefore we don't
576: // set it. Check for a value as well, as it can format data from
577: // some other source
578: if ( $this->_setValue === null && ! $this->_inData( $this->name(), $data ) ) {
579: return false;
580: }
581:
582: // In the data set, so use it
583: return true;
584: }
585: }
586:
587:
588: /**
589: * Execute the ipOpts to get the list of options to return to the client-
590: * side
591: *
592: * @param \DataTables\Database $db Database instance
593: * @return Array Array of value / label options for the list
594: * @internal
595: */
596: public function optionsExec ( $db )
597: {
598: if ( $this->_optsFn ) {
599: $fn = $this->_optsFn;
600: return $fn();
601: }
602: else if ( $this->_opts ) {
603: return $this->_opts->exec( $db );
604: }
605:
606: return false;
607: }
608:
609: /**
610: * Execute the spopts to get the list of options for SearchPanes to return
611: * to the client-side
612: *
613: * @param DataTables\Field $field The field to retrieve the data from
614: * @param DataTables\Editor $editor The editor instance
615: * @param DataTables\DTRequest $http The http request sent to the server
616: * @param DataTables\Field[] $fields All of the fields
617: * @param any $leftJoin Info for a leftJoin if required
618: * @return Promise<IOption[]> | boolean
619: * @internal
620: */
621: public function searchPaneOptionsExec ( $field, $editor, $http, $fields, $leftJoin)
622: {
623: if ( $this->_spoptsFn ) {
624: $fn = $this->_spoptsFn;
625: return $fn($editor->db(), $editor);
626: }
627: else if ( $this->_spopts ) {
628: return $this->_spopts->exec( $field, $editor, $http, $fields, $leftJoin );
629: }
630:
631: return false;
632: }
633:
634:
635: /**
636: * Get the value of the field, taking into account if it is coming from the
637: * DB or from a POST. If formatting has been specified for this field, it
638: * will be applied here.
639: *
640: * Called by the Editor / Join class instances - not expected for general
641: * consumption - internal.
642: * @param string $direction Direction that the data is travelling - 'get' is
643: * reading data, and 'set' is writing it to the DB.
644: * @param array $data Data submitted from the client-side when setting or the
645: * data for the row when getting data from the DB.
646: * @return string Value for the field
647: * @internal
648: */
649: public function val ( $direction, $data )
650: {
651: if ( $direction === 'get' ) {
652: if ( $this->_getValue !== null ) {
653: $val = $this->_getAssignedValue( $this->_getValue );
654: }
655: else {
656: // Getting data, so the db field name
657: $val = isset( $data[ $this->_dbField ] ) ?
658: $data[ $this->_dbField ] :
659: null;
660: }
661:
662: return $this->_format(
663: $val, $data, $this->_getFormatter, $this->_getFormatterOpts
664: );
665: }
666: else {
667: // Sanity check that we aren't operating on a function
668: if ( strpos( $this->dbField(), '(' ) !== false ) {
669: throw new \Exception('Cannot set the value for an SQL function field. These fields are read only.');
670: }
671:
672: // Setting data, so using from the payload (POST usually) and thus
673: // use the 'name'
674: $val = $this->_setValue !== null ?
675: $this->_getAssignedValue( $this->_setValue ) :
676: $this->_readProp( $this->name(), $data );
677:
678: // XSS removal / checker
679: if ( $this->_xssFormat ) {
680: $val = $this->xssSafety( $val );
681: }
682:
683: return $this->_format(
684: $val, $data, $this->_setFormatter, $this->_setFormatterOpts
685: );
686: }
687: }
688:
689:
690: /**
691: * Check the validity of the field based on the data submitted. Note that
692: * this validation is performed on the wire data - i.e. that which is
693: * submitted, before any setFormatter is run
694: *
695: * Called by the Editor / Join class instances - not expected for general
696: * consumption - internal.
697: *
698: * @param array $data Data submitted from the client-side
699: * @param Editor $editor Editor instance
700: * @param mixed $id Row id that is being validated
701: * @return boolean|string `true` if valid, string with error message if not
702: * @internal
703: */
704: public function validate ( $data, $editor, $id=null )
705: {
706: // Three cases for the validator - closure, string or null
707: if ( ! count( $this->_validator ) ) {
708: return true;
709: }
710:
711: $val = $this->_readProp( $this->name(), $data );
712: $processData = $editor->inData();
713: $instances = array(
714: 'action' => $processData['action'],
715: 'id' => $id,
716: 'field' => $this,
717: 'editor' => $editor,
718: 'db' => $editor->db()
719: );
720:
721: for ( $i=0, $ien=count( $this->_validator ) ; $i<$ien ; $i++ ) {
722: $validator = $this->_validator[$i];
723:
724: // Backwards compatibility
725: if ( is_string( $validator['func'] ) ) {
726: if ( strpos($validator['func'], "Validate::") === 0 ) {
727: $a = explode("::", $validator['func']);
728:
729: // Validate class static methods - they have `Legacy` counter parts that
730: // convert from the old style to the new so the old style options still work.
731: if ( method_exists( "\\DataTables\\Editor\\".$a[0], $a[1].'Legacy' ) ) {
732: $func = call_user_func( "\\DataTables\\Editor\\".$validator['func'].'Legacy', $validator['opts'] );
733: $res = call_user_func( $func, $val, $data, $this, $instances );
734: }
735: else {
736: // User style legacy function. Call it directly
737: $func = "\\DataTables\\Editor\\".$validator['func'];
738: $res = call_user_func( $func, $val, $data, $this, $instances );
739: }
740: }
741: else {
742: // And for cases where a string was used to point to a function,
743: // which was not in the Validate class
744: $res = call_user_func( $validator['func'], $val, $data, $validator['opts'], $instances );
745: }
746: }
747: else {
748: $func = $validator['func'];
749: $res = $func( $val, $data, $this, $instances );
750: }
751:
752: // Check if there was a validation error and if so, return it
753: if ( $res !== true ) {
754: return $res;
755: }
756: }
757:
758: // Validation methods all run, must be valid
759: return true;
760: }
761:
762:
763: /**
764: * Write the value for this field to the output array for a read operation
765: *
766: * @param array $out Row output data (to the JSON)
767: * @param mixed $srcData Row input data (raw, from the database)
768: * @internal
769: */
770: public function write( &$out, $srcData )
771: {
772: $this->_writeProp( $out, $this->name(), $this->val('get', $srcData) );
773: }
774:
775:
776: /**
777: * Perform XSS prevention on an input.
778: *
779: * @param mixed $val Value to be escaped
780: * @return string Safe value
781: */
782: public function xssSafety ( $val ) {
783: $xss = $this->_xss;
784:
785: if ( is_array( $val ) ) {
786: $res = array();
787:
788: foreach ( $val as $individual ) {
789: $res[] = $xss ?
790: $xss( $individual ) :
791: DataTables\Vendor\Htmlaw::filter( $individual );
792: }
793:
794: return $res;
795: }
796:
797: return $xss ?
798: $xss( $val ) :
799: DataTables\Vendor\Htmlaw::filter( $val );
800: }
801:
802:
803:
804: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
805: * Private methods
806: */
807:
808: /**
809: * Apply a formatter to data. The caller will decide what formatter to apply
810: * (get or set)
811: *
812: * @param mixed $val Value to be formatted
813: * @param mixed $data Full row data
814: * @param callable $formatter Formatting function to be called
815: * @param array $opts Array of options to be passed to the formatter
816: * @return mixed Formatted value
817: */
818: private function _format( $val, $data, $formatter, $opts )
819: {
820: // Three cases for the formatter - closure, string or null
821: if ( ! $formatter ) {
822: return $val;
823: }
824:
825: if ( ! is_string( $formatter ) ) {
826: return $formatter( $val, $data, $opts );
827: }
828:
829: // Backwards compatibility - strings will not be supported in v2
830: if ( strpos($formatter, "Format::") === 0 ) {
831: $a = explode( '::', $formatter );
832:
833: // Old style Editor formatter - use the legacy functions to
834: // convert to the new style
835: if ( method_exists( "\\DataTables\\Editor\\".$a[0], $a[1].'Legacy' ) ) {
836: $func = call_user_func( "\\DataTables\\Editor\\".$formatter.'Legacy', $opts );
837:
838: return $func( $val, $data );
839: }
840: else {
841: // User added old style methods
842: return call_user_func( "\\DataTables\\Editor\\".$formatter, $val, $data, $opts );
843: }
844: }
845:
846: // User function (string identifier)
847: return call_user_func( $formatter, $val, $data, $opts );
848: }
849:
850: /**
851: * Get the value from `_[gs]etValue` - taking into account if it is callable
852: * function or not
853: *
854: * @param mixed $val Value to be evaluated
855: * @return mixed Value assigned, or returned from the function
856: */
857: private function _getAssignedValue ( $val )
858: {
859: return is_callable($val) && is_object($val) ?
860: $val() :
861: $val;
862: }
863:
864: /**
865: * Check is a parameter is in the submitted data set. This is functionally
866: * the same as the `_readProp()` method, but in this case a binary value
867: * is required to indicate if the value is present or not.
868: *
869: * @param string $name Javascript dotted object name to write to
870: * @param array $data Data source array to read from
871: * @return boolean `true` if present, `false` otherwise
872: * @private
873: */
874: private function _inData ( $name, $data )
875: {
876: if ( strpos($name, '.') === false ) {
877: return isset( $data[ $name ] ) ?
878: true :
879: false;
880: }
881:
882: $names = explode( '.', $name );
883: $inner = $data;
884:
885: for ( $i=0 ; $i<count($names)-1 ; $i++ ) {
886: if ( ! isset( $inner[ $names[$i] ] ) ) {
887: return false;
888: }
889:
890: $inner = $inner[ $names[$i] ];
891: }
892:
893: return isset( $inner [ $names[count($names)-1] ] ) ?
894: true :
895: false;
896: }
897: }
898:
899: