From e1966dd3377ec6d0ce8d0c98d3d4a03dfe237412 Mon Sep 17 00:00:00 2001 From: Steve Gallo Date: Wed, 1 Feb 2017 16:04:02 -0500 Subject: [PATCH 1/3] Make all aggregation period data available as bind parameters to query --- classes/ETL/Aggregator/pdoAggregator.php | 120 ++++++++++++++--------- 1 file changed, 75 insertions(+), 45 deletions(-) diff --git a/classes/ETL/Aggregator/pdoAggregator.php b/classes/ETL/Aggregator/pdoAggregator.php index 322978ee50..eb4bb785dd 100644 --- a/classes/ETL/Aggregator/pdoAggregator.php +++ b/classes/ETL/Aggregator/pdoAggregator.php @@ -17,6 +17,8 @@ * ${:PERIOD_SECONDS} The number of seconds in the current aggregation period being processed. * ${:PERIOD_VALUE} The value of the aggregation period within the year (e.g., day of year, month of year, etc.) * ${:PERIOD_ID} Identifier into the aggregation period tables (e.g., days, months, etc.) + * ${:PERIOD_START} Start datetime of the current period + * ${:PERIOD_END} End datetime of the current period * ${:PERIOD_START_TS} Start timestamp of the current period * ${:PERIOD_END_TS} End timestamp of the current period * @@ -283,6 +285,10 @@ protected function initialize() ':PERIOD_VALUE' => ":period_value", // Identifier into the aggregation period tables (e.g., days, months, etc.) ':PERIOD_ID' => ":period_id", + // Start datetime of the period + ':PERIOD_START' => ":period_start", + // End datetime of the period + ':PERIOD_END' => ":period_end", // Start timestamp of the period ':PERIOD_START_TS' => ":period_start_ts", // End timestamp of the period @@ -668,7 +674,7 @@ protected function getDirtyAggregationPeriods($aggregationUnit) $sql = "SELECT distinct d.id as period_id, - d.`year` as year_id, + d.`year` as year_value, d.`${aggregationUnit}` as period_value, d.${aggregationUnit}_start as period_start, d.${aggregationUnit}_end as period_end, @@ -690,7 +696,8 @@ protected function getDirtyAggregationPeriods($aggregationUnit) try { $this->logger->debug("Select dirty aggregation periods:\n$sql"); if ( ! $this->etlOverseerOptions->isDryrun() ) { - $result = $this->utilityHandle->query($sql); + // $result = $this->utilityHandle->query($sql); + $result = $this->sourceHandle->query($sql); } } catch (PDOException $e) { $this->logAndThrowSqlException($sql, $e, "Error querying dirty date ids"); @@ -888,25 +895,23 @@ protected function _execute($aggregationUnit) $batchStartTime = microtime(true); - $dateIdSlice = array_slice($aggregationPeriodList, $aggregationPeriodListOffset, $batchSliceSize); - if ( count($dateIdSlice) == 0 ) { + $aggregationPeriodSlice = array_slice($aggregationPeriodList, $aggregationPeriodListOffset, $batchSliceSize); + if ( count($aggregationPeriodSlice) == 0 ) { break; } // Is this the last slice? - $done = ( count($dateIdSlice) < $batchSliceSize ); + $done = ( count($aggregationPeriodSlice) < $batchSliceSize ); // Find the min/max time range and day id for this slice so we know which jobs to // include in the temporary table. - $firstSlice = current($dateIdSlice); - $lastSlice = end($dateIdSlice); - reset($dateIdSlice); + $firstSlice = current($aggregationPeriodSlice); + $lastSlice = end($aggregationPeriodSlice); + reset($aggregationPeriodSlice); // Note that slices are ordered newest to oldest - $minPeriodSeconds = $lastSlice['period_start_ts']; - $maxPeriodSeconds = $firstSlice['period_end_ts']; $minDayId = $lastSlice['period_start_day_id']; $maxDayId = $firstSlice['period_end_day_id']; $minPeriodId = $lastSlice['period_id']; @@ -937,13 +942,46 @@ protected function _execute($aggregationUnit) // A subset of the bind variables are available. We should check to see if there // are more that we can't handle. - $availableParams = array( - ":period_start_ts" => $minPeriodSeconds, - ":period_end_ts" => $maxPeriodSeconds, - ":period_start_day_id" => $minDayId, - ":period_end_day_id" => $maxDayId + // We are taking the WHERE clause from source query so we need to + // support all bind parameters available to that query. Min and max + // information will need to come from the last and first slice, + // respecitively (see note below). + + $availableParamKeys = array_map( + function ($k) { + return ":$k"; + }, + array_keys($firstSlice) ); + // NOTE 1 + // + // The aggregation periods are supplied in reverse order so newer + // periods are processed first. This means we need to invert the + // start/end slices. + // + // NOTE 2 + // + // Since the WHERE clause was meant to be used with a SINGLE + // aggregation period, parameters such as period_hours and + // period_seconds are not summed and chosen arbitrarily from one of + // the periods. Similarly, period_id and period_value are chosen + // arbitrarily. + + $availableParamValues = array_map( + function ($k, $first, $last) { + if ( FALSE !== strpos($k, '_end') || FALSE !== strpos($k, 'end_') ) { + return $first; + } else { + return $last; + } + }, + array_keys($firstSlice), + $firstSlice, + $lastSlice); + + $availableParams = array_combine($availableParamKeys, $availableParamValues); + $bindParams = array(); preg_match_all($bindParamRegex, $whereClause, $matches); $bindParams = $matches[0]; @@ -963,16 +1001,18 @@ protected function _execute($aggregationUnit) $this->processAggregationPeriods( $aggregationUnit, - $dateIdSlice, + $aggregationPeriodSlice, $selectStmt, $insertStmt, $discoveredBindParams, $numAggregationPeriods, $aggregationPeriodListOffset); - $this->logger->info("[EXPERIMENTAL] Total time for batch (day_id $minDayId - $maxDayId): " - . round((microtime(true) - $batchStartTime), 2) . "s " - . "(" . round((microtime(true) - $batchStartTime) / count($dateIdSlice), 3) . "s/period)"); + $this->logger->info( + "[EXPERIMENTAL] Total time for batch (day_id $minDayId - $maxDayId): " + . round((microtime(true) - $batchStartTime), 2) . "s " + . "(" . round((microtime(true) - $batchStartTime) / count($aggregationPeriodSlice), 3) . "s/period)" + ); $aggregationPeriodListOffset += $batchSliceSize; @@ -1043,32 +1083,22 @@ protected function processAggregationPeriods( $optimize = $this->allowSingleDatabaseOptimization(); $numPeriodsProcessed = 0; - foreach ($aggregationPeriodList as $date_result) { + foreach ($aggregationPeriodList as $aggregationPeriodInfo) { $dateIdStartTime = microtime(true); $numRecords = 0; - $period_id = $date_result['period_id']; - $year_id = $date_result['year_id']; - $period_value = $date_result["period_value"]; - $period_seconds = $date_result["period_seconds"]; - $period_start_ts = $date_result["period_start_ts"]; - $period_end_ts = $date_result["period_end_ts"]; - $period_start_day_id = $date_result["period_start_day_id"]; - $period_end_day_id = $date_result["period_end_day_id"]; - - // Generate a list of available parameters and prune these based on the parameters - // discovered in the SQL. - - $availableParams = array( - ":period_id" => $period_id, - ":year_value" => $year_id, - ":period_value" => $period_value, - ":period_seconds" => $period_seconds, - ":period_start_ts" => $period_start_ts, - ":period_end_ts" => $period_end_ts, - ":period_start_day_id" => $period_start_day_id, - ":period_end_day_id" => $period_end_day_id - ); + // Make all of the data for each aggregation period available to the + // query. Change the array keys into bind parameters. + + $availableParamKeys = array_map( + function ($k) { + return ":$k"; + }, + array_keys($aggregationPeriodInfo) + ); + + $availableParams = array_combine($availableParamKeys, $aggregationPeriodInfo); + $periodId = $aggregationPeriodInfo['period_id']; // If we're not completely re-aggregating, delete existing entries from the aggregation table // matching the periods that we are aggregating. Be sure to restrict resources if necessary. @@ -1099,7 +1129,7 @@ protected function processAggregationPeriods( foreach ( $this->etlDestinationTableList as $etlTableKey => $etlTable ) { $qualifiedDestTableName = $etlTable->getFullName(); - $deleteSql = "DELETE FROM $qualifiedDestTableName WHERE {$aggregationUnit}_id = $period_id"; + $deleteSql = "DELETE FROM $qualifiedDestTableName WHERE {$aggregationUnit}_id = $periodId"; if ( count($restrictions) > 0 ) { $deleteSql .= " AND " . implode(" AND ", $dummyQuery->getOverseerRestrictionValues()); @@ -1116,7 +1146,7 @@ protected function processAggregationPeriods( // Perform aggregation on this aggregation period - $this->logger->debug("Aggregating $aggregationUnit $period_id"); + $this->logger->debug("Aggregating $aggregationUnit $periodId"); if ( $optimize ) { @@ -1164,7 +1194,7 @@ protected function processAggregationPeriods( $numPeriodsProcessed++; $this->logger->info("Aggregated $aggregationUnit (" . ( $numPeriodsProcessed + $aggregationPeriodOffset) - . " of $totalNumAggregationPeriods) $period_id records = $numRecords, time = " . + . " of $totalNumAggregationPeriods) $periodId records = $numRecords, time = " . round((microtime(true) - $dateIdStartTime), 2) . "s"); } // foreach ($aggregationPeriodList as $date_result) From 16bd38c8bbc8c55b3a20762ac76e7d4f4d1246b3 Mon Sep 17 00:00:00 2001 From: Steve Gallo Date: Wed, 1 Feb 2017 16:24:20 -0500 Subject: [PATCH 2/3] Fix phpcs issues --- classes/ETL/Aggregator/pdoAggregator.php | 125 ++++++++++++----------- 1 file changed, 67 insertions(+), 58 deletions(-) diff --git a/classes/ETL/Aggregator/pdoAggregator.php b/classes/ETL/Aggregator/pdoAggregator.php index eb4bb785dd..43a556e516 100644 --- a/classes/ETL/Aggregator/pdoAggregator.php +++ b/classes/ETL/Aggregator/pdoAggregator.php @@ -159,7 +159,7 @@ public function verify(EtlOverseerOptions $etlOptions = null) if ( null === $this->etlSourceQuery ) { $msg = "ETL source query is not set"; $this->logAndThrowException($msg); - } else if ( ! $this->etlSourceQuery instanceof Query ) { + } elseif ( ! $this->etlSourceQuery instanceof Query ) { $msg = "ETL source query is not an instance of Query"; $this->logAndThrowException($msg); } @@ -235,9 +235,11 @@ protected function initialize() } $this->logger->debug("Create ETL destination aggregation table object"); - $this->etlDestinationTable = new AggregationTable($this->parsedDefinitionFile->table_definition, - $this->destinationEndpoint->getSystemQuoteChar(), - $this->logger); + $this->etlDestinationTable = new AggregationTable( + $this->parsedDefinitionFile->table_definition, + $this->destinationEndpoint->getSystemQuoteChar(), + $this->logger + ); $this->etlDestinationTable->setSchema($this->destinationEndpoint->getSchema()); if ( isset($this->options->table_prefix) && @@ -266,8 +268,10 @@ protected function initialize() } $this->logger->debug("Create ETL source query object"); - $this->etlSourceQuery = new Query($this->parsedDefinitionFile->source_query, - $this->sourceEndpoint->getSystemQuoteChar()); + $this->etlSourceQuery = new Query( + $this->parsedDefinitionFile->source_query, + $this->sourceEndpoint->getSystemQuoteChar() + ); } // ( null === $this->etlSourceQuery ) // -------------------------------------------------------------------------------- @@ -297,7 +301,7 @@ protected function initialize() ':PERIOD_START_DAY_ID' => ":period_start_day_id", // The day_id of the end of this period ':PERIOD_END_DAY_ID' => ":period_end_day_id" - ); + ); $this->variableMap = array_merge($this->variableMap, $parameters); @@ -570,41 +574,41 @@ protected function getDirtyAggregationPeriods($aggregationUnit) $endDayIdToUnitId = null; switch ( $aggregationUnit ) { - case 'day': - // No conversion needed - $unitIdToStartDayId = "d.id"; - $unitIdToEndDayId = "d.id"; - $startDayIdToUnitId = $startDayIdField; - $endDayIdToUnitId = $endDayIdField; - break; - case 'month': - $unitIdToStartDayId = "d.`year`*100000 + DAYOFYEAR(concat(d.`year`, '-', d.`month`, '-01'))"; - $unitIdToEndDayId = "d.`year`*100000 + DAYOFYEAR(date_sub(date_add(concat(d.`year`, '-', d.`month`, '-01'), interval 1 month), interval 1 day))"; - $startDayIdToUnitId = "truncate($startDayIdField/100000, 0)*100000 + " - . "MONTH(date_add(concat(truncate($startDayIdField/100000, 0), '-01-01'), interval (mod($startDayIdField, 1000) - 1) day))"; - $endDayIdToUnitId = "truncate($endDayIdField/100000, 0)*100000 + " - . "MONTH(date_add(concat(truncate($endDayIdField/100000, 0), '-01-01'), interval (mod($endDayIdField, 1000) - 1) day))"; - break; - case 'quarter': - $unitIdToStartDayId = "d.`year`*100000 + DAYOFYEAR(date_add(concat(d.`year`, '-01-01'), interval (d.`quarter` - 1) quarter))"; - $unitIdToEndDayId = "d.`year`*100000 + DAYOFYEAR(date_sub(date_add(concat(d.`year`, '-01-01'), interval d.`quarter` quarter), interval 1 day))"; - $startDayIdToUnitId = "truncate($startDayIdField/100000, 0)*100000 + " - . "QUARTER(date_add(concat(truncate($startDayIdField/100000, 0), '-01-01'), interval (mod($startDayIdField, 1000) - 1) day))"; - $endDayIdToUnitId = "truncate($endDayIdField/100000, 0)*100000 + " - . "QUARTER(date_add(concat(truncate($endDayIdField/100000, 0), '-01-01'), interval (mod($endDayIdField, 1000) - 1) day))"; - break; - case 'year': - $unitIdToStartDayId = "d.`year`*100000 + DAYOFYEAR(concat(d.`year`, '-01-01'))"; - $unitIdToEndDayId = "d.`year`*100000 + DAYOFYEAR(date_sub(date_add(concat(d.`year`, '-01-01'), interval 1 year), interval 1 day))"; - $startDayIdToUnitId = "truncate($startDayIdField/100000, 0)*100000"; - $endDayIdToUnitId = "truncate($endDayIdField/100000, 0)*100000"; - break; - default: - // We should never get here because we are verifying the existance of the aggregation - // unit tables in performPreAggregationUnitTasks. - $msg = "Unsupported aggregation unit '$aggregationUnit'"; - $this->logAndThrowException($msg); - break; + case 'day': + // No conversion needed + $unitIdToStartDayId = "d.id"; + $unitIdToEndDayId = "d.id"; + $startDayIdToUnitId = $startDayIdField; + $endDayIdToUnitId = $endDayIdField; + break; + case 'month': + $unitIdToStartDayId = "d.`year`*100000 + DAYOFYEAR(concat(d.`year`, '-', d.`month`, '-01'))"; + $unitIdToEndDayId = "d.`year`*100000 + DAYOFYEAR(date_sub(date_add(concat(d.`year`, '-', d.`month`, '-01'), interval 1 month), interval 1 day))"; + $startDayIdToUnitId = "truncate($startDayIdField/100000, 0)*100000 + " + . "MONTH(date_add(concat(truncate($startDayIdField/100000, 0), '-01-01'), interval (mod($startDayIdField, 1000) - 1) day))"; + $endDayIdToUnitId = "truncate($endDayIdField/100000, 0)*100000 + " + . "MONTH(date_add(concat(truncate($endDayIdField/100000, 0), '-01-01'), interval (mod($endDayIdField, 1000) - 1) day))"; + break; + case 'quarter': + $unitIdToStartDayId = "d.`year`*100000 + DAYOFYEAR(date_add(concat(d.`year`, '-01-01'), interval (d.`quarter` - 1) quarter))"; + $unitIdToEndDayId = "d.`year`*100000 + DAYOFYEAR(date_sub(date_add(concat(d.`year`, '-01-01'), interval d.`quarter` quarter), interval 1 day))"; + $startDayIdToUnitId = "truncate($startDayIdField/100000, 0)*100000 + " + . "QUARTER(date_add(concat(truncate($startDayIdField/100000, 0), '-01-01'), interval (mod($startDayIdField, 1000) - 1) day))"; + $endDayIdToUnitId = "truncate($endDayIdField/100000, 0)*100000 + " + . "QUARTER(date_add(concat(truncate($endDayIdField/100000, 0), '-01-01'), interval (mod($endDayIdField, 1000) - 1) day))"; + break; + case 'year': + $unitIdToStartDayId = "d.`year`*100000 + DAYOFYEAR(concat(d.`year`, '-01-01'))"; + $unitIdToEndDayId = "d.`year`*100000 + DAYOFYEAR(date_sub(date_add(concat(d.`year`, '-01-01'), interval 1 year), interval 1 day))"; + $startDayIdToUnitId = "truncate($startDayIdField/100000, 0)*100000"; + $endDayIdToUnitId = "truncate($endDayIdField/100000, 0)*100000"; + break; + default: + // We should never get here because we are verifying the existance of the aggregation + // unit tables in performPreAggregationUnitTasks. + $msg = "Unsupported aggregation unit '$aggregationUnit'"; + $this->logAndThrowException($msg); + break; } // switch ( $aggregationUnit ) if ( $this->etlOverseerOptions->isForce() ) { @@ -656,7 +660,7 @@ protected function getDirtyAggregationPeriods($aggregationUnit) $query->overseer_restrictions = $aggregationPeriodQueryOptions->overseer_restrictions; } else { $msg = "No restrictions on selection of periods to aggregate. " - . "Re-aggregating all records in " . $sourceSchema . "." . $tableName; + . "Re-aggregating all records in " . $sourceSchema . "." . $tableName; $this->logger->notice($msg); } @@ -784,7 +788,7 @@ protected function _execute($aggregationUnit) } $this->etlSourceQueryModified = true; - } else if ( ! $enableBatchAggregation && $this->etlSourceQueryModified ) { + } elseif ( ! $enableBatchAggregation && $this->etlSourceQueryModified ) { $this->logger->info("[EXPERIMENTAL] Restore original first table"); @@ -876,7 +880,8 @@ protected function _execute($aggregationUnit) $selectStmt, $insertStmt, $discoveredBindParams, - $numAggregationPeriods); + $numAggregationPeriods + ); } else { @@ -970,7 +975,7 @@ function ($k) { $availableParamValues = array_map( function ($k, $first, $last) { - if ( FALSE !== strpos($k, '_end') || FALSE !== strpos($k, 'end_') ) { + if ( false !== strpos($k, '_end') || false !== strpos($k, 'end_') ) { return $first; } else { return $last; @@ -978,7 +983,8 @@ function ($k, $first, $last) { }, array_keys($firstSlice), $firstSlice, - $lastSlice); + $lastSlice + ); $availableParams = array_combine($availableParamKeys, $availableParamValues); @@ -1006,7 +1012,8 @@ function ($k, $first, $last) { $insertStmt, $discoveredBindParams, $numAggregationPeriods, - $aggregationPeriodListOffset); + $aggregationPeriodListOffset + ); $this->logger->info( "[EXPERIMENTAL] Total time for batch (day_id $minDayId - $maxDayId): " @@ -1038,7 +1045,7 @@ function ($k, $first, $last) { "start_time" => $time_start, "end_time" => $time_end, "elapsed_time" => round($time, 5) - )); + )); return $numAggregationPeriods; @@ -1074,8 +1081,8 @@ protected function processAggregationPeriods( PDOStatement $insertStmt, array $discoveredBindParams, $totalNumAggregationPeriods, - $aggregationPeriodOffset = 0) - { + $aggregationPeriodOffset = 0 + ) { if ( $this->etlOverseerOptions->isDryrun() ) { return 0; } @@ -1280,12 +1287,15 @@ protected function buildSqlStatements($aggregationUnit, $includeSchema = true) "(" . implode(",\n", array_keys($this->etlSourceQuery->getRecords())) . ")\nVALUES\n(" . - implode(",\n", - array_map(function ($s) { - return ":$s"; - }, - array_keys($this->etlSourceQuery->getRecords())) - ) . + implode( + ",\n", + array_map( + function ($s) { + return ":$s"; + }, + array_keys($this->etlSourceQuery->getRecords()) + ) + ) . ")"; $this->optimizedInsertSql = "INSERT INTO " . $this->etlDestinationTable->getFullName($includeSchema) . "\n" . @@ -1309,5 +1319,4 @@ protected function buildSqlStatements($aggregationUnit, $includeSchema = true) return true; } // buildSqlStatements() - } // class pdoAggregator From 767fbdd5b0388587d9b5e3749fbe45260b5f271d Mon Sep 17 00:00:00 2001 From: Steve Gallo Date: Thu, 2 Feb 2017 13:07:05 -0500 Subject: [PATCH 3/3] Addressed code review comments --- classes/ETL/Aggregator/pdoAggregator.php | 33 +++++---------------- classes/ETL/Utilities.php | 37 +++++++++++++++++++----- 2 files changed, 37 insertions(+), 33 deletions(-) diff --git a/classes/ETL/Aggregator/pdoAggregator.php b/classes/ETL/Aggregator/pdoAggregator.php index 43a556e516..3b328b507f 100644 --- a/classes/ETL/Aggregator/pdoAggregator.php +++ b/classes/ETL/Aggregator/pdoAggregator.php @@ -952,12 +952,7 @@ protected function _execute($aggregationUnit) // information will need to come from the last and first slice, // respecitively (see note below). - $availableParamKeys = array_map( - function ($k) { - return ":$k"; - }, - array_keys($firstSlice) - ); + $availableParamKeys = Utilities::createPdoBindVarsFromArrayKeys($firstSlice); // NOTE 1 // @@ -1097,13 +1092,7 @@ protected function processAggregationPeriods( // Make all of the data for each aggregation period available to the // query. Change the array keys into bind parameters. - $availableParamKeys = array_map( - function ($k) { - return ":$k"; - }, - array_keys($aggregationPeriodInfo) - ); - + $availableParamKeys = Utilities::createPdoBindVarsFromArrayKeys($aggregationPeriodInfo); $availableParams = array_combine($availableParamKeys, $aggregationPeriodInfo); $periodId = $aggregationPeriodInfo['period_id']; @@ -1284,19 +1273,11 @@ protected function buildSqlStatements($aggregationUnit, $includeSchema = true) $this->selectSql = $this->etlSourceQuery->getSelectSql($includeSchema); $this->insertSql = "INSERT INTO " . $this->etlDestinationTable->getFullName($includeSchema) . "\n" . - "(" . - implode(",\n", array_keys($this->etlSourceQuery->getRecords())) - . ")\nVALUES\n(" . - implode( - ",\n", - array_map( - function ($s) { - return ":$s"; - }, - array_keys($this->etlSourceQuery->getRecords()) - ) - ) . - ")"; + "(" + . implode(",\n", array_keys($this->etlSourceQuery->getRecords())) + . ")\nVALUES\n(" + . implode(",\n", Utilities::createPdoBindVarsFromArrayKeys($this->etlSourceQuery->getRecords())) + . ")"; $this->optimizedInsertSql = "INSERT INTO " . $this->etlDestinationTable->getFullName($includeSchema) . "\n" . "(" . diff --git a/classes/ETL/Utilities.php b/classes/ETL/Utilities.php index a4b5f30af0..8cac329061 100644 --- a/classes/ETL/Utilities.php +++ b/classes/ETL/Utilities.php @@ -51,8 +51,8 @@ public static function substituteVariables( $string, array $map, array &$substitutedVariables = null, - array &$unsubstitutedVariables = null ) - { + array &$unsubstitutedVariables = null + ) { // If we are not tracking the variables that have or have not been substituted, simply // perform a string replacement. @@ -68,13 +68,17 @@ public static function substituteVariables( // Track variables that have been substituted - array_map(function ($v, $k) use (&$string, &$localSubstituted) { + array_map( + function ($v, $k) use (&$string, &$localSubstituted) { $search = '${' . $k . '}'; if ( false !== strpos($string, $search) ) { $substituted[] = $k; } $string = str_replace($search, $v, $string); - }, $map, array_keys($map)); + }, + $map, + array_keys($map) + ); // If there are any variables left in the string, track them as unsubstituted. @@ -145,7 +149,7 @@ public static function processMacro($string, stdClass $config) if ( ! isset($paths->macro_dir) ) { $msg = __CLASS__ . ": ETL configuration paths.macro_dir is not set"; throw new Exception($msg); - } else if ( ! is_dir($paths->macro_dir) ) { + } elseif ( ! is_dir($paths->macro_dir) ) { $msg = __CLASS__ . ": ETL configuration paths.macro_dir '{$paths->macro_dir}' is not a directory"; throw new Exception($msg); } @@ -172,7 +176,7 @@ public static function processMacro($string, stdClass $config) if ( ! is_file($filename) ) { $msg = __CLASS__ . ": Cannot load macro file '$filename'"; throw new Exception($msg); - } else if ( 0 == filesize($filename) ) { + } elseif ( 0 == filesize($filename) ) { // No use processing an empty macro return; } @@ -187,7 +191,7 @@ public static function processMacro($string, stdClass $config) $stripped = array(); foreach ( explode("\n", $macro) as $line ) { - if ( 0 === strpos($line, "--") || 0 === strpos($line, "#") ) { + if ( 0 === strpos($line, "--") || 0 === strpos($line, "#") ) { continue; } $stripped[] = $line; @@ -226,4 +230,23 @@ public static function filterBooleanVar($value, $filter = FILTER_VALIDATE_BOOLEA return ( false === $value ? false : filter_var($value, $filter, $options) ); } // filterBooleanVar() + /* ------------------------------------------------------------------------------------------ + * Generate an array of strings that can be used as PDO bind parameters (e.g., :var) + * using the keys from the source array. + * + * @param $source An associative array whose keys will be used to generate bind parameters + * + * @return An array containing the keys of $source pre-pended with ":" + * ------------------------------------------------------------------------------------------ + */ + + public static function createPdoBindVarsFromArrayKeys(array $source) + { + return array_map( + function ($key) { + return ":$key"; + }, + array_keys($source) + ); + } // createPdoBindVarsFromArrayKeys() } // class Utilities