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