From 9bbcf033e6f0e3629763b37ad791d7b899b0a08f Mon Sep 17 00:00:00 2001 From: Patrick Brouwers Date: Thu, 10 Dec 2015 19:07:26 +0100 Subject: [PATCH] Built in support for auto-queueing chunked imports --- .gitignore | 2 +- composer.json | 5 +- docs/import/chunk.md | 20 + dump.rdb | 1 + phpunit.xml | 3 + .../Excel/Readers/ChunkedReadJob.php | 114 ++++++ .../Excel/Readers/LaravelExcelReader.php | 366 ++++++++++++------ tests/Filters/ChunkReadFilterTest.php | 130 +++---- tests/Filters/log.txt | 1 + tests/Filters/rounds.txt | 1 + tests/TestCase.php | 3 +- 11 files changed, 447 insertions(+), 199 deletions(-) create mode 100644 dump.rdb create mode 100644 src/Maatwebsite/Excel/Readers/ChunkedReadJob.php create mode 100644 tests/Filters/log.txt create mode 100644 tests/Filters/rounds.txt diff --git a/.gitignore b/.gitignore index 2c1fc0c14..582640226 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ /vendor composer.phar composer.lock -.DS_Store \ No newline at end of file +.DS_Store diff --git a/composer.json b/composer.json index 6f73626a9..39063b2fd 100644 --- a/composer.json +++ b/composer.json @@ -31,12 +31,13 @@ "phpseclib/phpseclib": "~1.0", "phpunit/phpunit": "~4.0", "mockery/mockery": "~0.9", - "orchestra/testbench": "3.0.*" + "orchestra/testbench": "3.1.*" }, "suggest": { "illuminate/http": "5.0.*|5.1.*|5.2.*", "illuminate/routing": "5.0.*|5.1.*|5.2.*", - "illuminate/view": "5.0.*|5.1.*|5.2.*" + "illuminate/view": "5.0.*|5.1.*|5.2.*", + "illuminate/queue": "5.0.*|5.1.*|5.2.*" }, "autoload": { "classmap": [ diff --git a/docs/import/chunk.md b/docs/import/chunk.md index e40cce994..d4deff932 100644 --- a/docs/import/chunk.md +++ b/docs/import/chunk.md @@ -41,3 +41,23 @@ Injected ExcelFile example: // or return true if you want to stop importing. }); } + +## Queued chunks + +We automatically queue every chunk for you, if you have enabled the queue driver in your config. + +``` + Excel::filter('chunk')->load(storage_path('test2.xls'))->chunk(10, function ($results) { + // This will be handled inside the queue + app('log')->info('Import: ' . count($results)); + }); +``` + +If you want to by-pass the behaviour, you can pass `false` as third param of `chunk($size, $callback, $shouldQueue)`. + +``` + Excel::filter('chunk')->load(storage_path('test2.xls'))->chunk(10, function ($results) { + // This will be handled inside the queue + app('log')->info('Import: ' . count($results)); + }, false); +``` diff --git a/dump.rdb b/dump.rdb new file mode 100644 index 000000000..56af04ea1 --- /dev/null +++ b/dump.rdb @@ -0,0 +1 @@ +REDIS0006ÿܳCðZÜòV \ No newline at end of file diff --git a/phpunit.xml b/phpunit.xml index 3347b75b7..a215f05ce 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -15,4 +15,7 @@ ./tests/ + + + diff --git a/src/Maatwebsite/Excel/Readers/ChunkedReadJob.php b/src/Maatwebsite/Excel/Readers/ChunkedReadJob.php new file mode 100644 index 000000000..2dd74929a --- /dev/null +++ b/src/Maatwebsite/Excel/Readers/ChunkedReadJob.php @@ -0,0 +1,114 @@ +startRow = $startRow; + $this->chunkSize = $chunkSize; + $this->startIndex = $startIndex; + $this->file = $file; + + $this->callback = $shouldQueue ? (new Serializer)->serialize($callback) : $callback; + $this->sheets = $sheets; + $this->shouldQueue = $shouldQueue; + } + + /*** + * Handle the read job + */ + public function handle() + { + $reader = app('excel.reader'); + $reader->injectExcel(app('phpexcel')); + $reader->_init($this->file); + + $filter = new ChunkReadFilter(); + $reader->reader->setLoadSheetsOnly($this->sheets); + $reader->reader->setReadFilter($filter); + $reader->reader->setReadDataOnly(true); + + // Set the rows for the chunking + $filter->setRows($this->startRow, $this->chunkSize); + + // Load file with chunk filter enabled + $reader->excel = $reader->reader->load($this->file); + + // Slice the results + $results = $reader->get()->slice($this->startIndex, $this->chunkSize); + + $callback = $this->shouldQueue ? (new Serializer)->unserialize($this->callback) : $this->callback; + + // Do a callback + if (is_callable($callback)) { + $break = call_user_func($callback, $results); + } + + $reader->_reset(); + unset($reader, $results); + + if ($break) { + return true; + } + } +} diff --git a/src/Maatwebsite/Excel/Readers/LaravelExcelReader.php b/src/Maatwebsite/Excel/Readers/LaravelExcelReader.php index 97ddb5ed4..53b04694d 100644 --- a/src/Maatwebsite/Excel/Readers/LaravelExcelReader.php +++ b/src/Maatwebsite/Excel/Readers/LaravelExcelReader.php @@ -2,6 +2,8 @@ use Cache; use Config; +use Illuminate\Foundation\Bus\DispatchesJobs; +use Illuminate\Support\Facades\Queue; use Maatwebsite\Excel\Classes\PHPExcel; use PHPExcel_Cell; use PHPExcel_IOFactory; @@ -13,7 +15,6 @@ use Maatwebsite\Excel\Exceptions\LaravelExcelException; /** - * * LaravelExcel Excel reader * * @category Laravel Excel @@ -23,153 +24,179 @@ * @author Maatwebsite * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL */ -class LaravelExcelReader { +class LaravelExcelReader +{ + use DispatchesJobs; /** * Excel object + * * @var \PHPExcel */ public $excel; /** * Spreadsheet writer + * * @var object */ public $reader; /** * The file to read + * * @var string */ public $file; /** * Selected columns + * * @var array */ - public $columns = array(); + public $columns = []; /** * Spreadsheet title + * * @var string */ public $title; /** * Default extension + * * @var string */ public $ext = 'xls'; /** * Encoding + * * @var boolean */ public $encoding = false; /** * Default format + * * @var stirng */ public $format; /** * The parsed file + * * @var SheetCollection|RowCollection */ public $parsed; /** * Calculate [true/false] + * * @var boolean */ public $calculate; /** * Limit data + * * @var boolean */ protected $limit = false; /** * Amount of rows to skip + * * @var integer */ protected $skip = 0; /** * Slug separator + * * @var string */ public $separator = false; /** * Ignore empty cells + * * @var boolean */ public $ignoreEmpty = false; /** * Format dates + * * @var boolean */ public $formatDates = true; /** * The date columns + * * @var array */ - public $dateColumns = array(); + public $dateColumns = []; /** * If the file has a heading or not + * * @var boolean */ public $noHeading = false; /** * Default date format + * * @var string */ public $dateFormat; /** * Whether the results are cached or not + * * @var boolean */ public $remembered = false; /** * Amount of minutes the results will remain cached + * * @var integer */ public $cacheMinutes = 10; /** * Selected sheets + * * @var array */ - public $selectedSheets = array(); + public $selectedSheets = []; /** * Selected sheet indices + * * @var array */ - public $selectedSheetIndices = array(); + public $selectedSheetIndices = []; /** * Active filter + * * @var PHPExcel_Reader_IReadFilter */ protected $filter; /** * Filters + * * @var array */ - public $filters = array( - 'registered' => array() - ); + public $filters = [ + 'registered' => [] + ]; /** * @var LaravelExcelWorksheet @@ -198,6 +225,7 @@ class LaravelExcelReader { /** * Construct new reader + * * @param Filesystem $filesystem * @param FormatIdentifier $identifier */ @@ -209,9 +237,11 @@ public function __construct(Filesystem $filesystem, FormatIdentifier $identifier /** * Load a file + * * @param string $file * @param string|boolean $encoding * @param bool $noBasePath + * * @return LaravelExcelReader */ public function load($file, $encoding = false, $noBasePath = false) @@ -220,8 +250,9 @@ public function load($file, $encoding = false, $noBasePath = false) $this->_init($file, $encoding, $noBasePath); // Only fetch selected sheets if necessary - if ($this->sheetsSelected()) + if ($this->sheetsSelected()) { $this->reader->setLoadSheetsOnly($this->selectedSheets); + } // Load the file $this->excel = $this->reader->load($this->file); @@ -232,7 +263,8 @@ public function load($file, $encoding = false, $noBasePath = false) /** * @param integer|callable|string $sheetID - * @param null $callback + * @param null $callback + * * @return $this * @throws \PHPExcel_Exception */ @@ -242,25 +274,21 @@ public function sheet($sheetID, $callback = null) $isCallable = false; // Init a new PHPExcel instance without any worksheets - if(!$this->excel instanceof PHPExcel) { + if (!$this->excel instanceof PHPExcel) { $this->original = $this->excel; $this->initClonedExcelObject($this->excel); // Clone all connected sheets - foreach($this->original->getAllSheets() as $sheet) - { + foreach ($this->original->getAllSheets() as $sheet) { $this->excel->createSheet()->cloneParent($sheet); } } // Copy the callback when needed - if(is_callable($sheetID)) - { - $callback = $sheetID; + if (is_callable($sheetID)) { + $callback = $sheetID; $isCallable = true; - } - elseif(is_callable($callback)) - { + } elseif (is_callable($callback)) { $isCallable = true; } @@ -268,8 +296,9 @@ public function sheet($sheetID, $callback = null) $this->sheet = $this->getSheetByIdOrName($sheetID); // Do the callback - if ($isCallable) + if ($isCallable) { call_user_func($callback, $this->sheet); + } // Return the sheet return $this->sheet; @@ -277,28 +306,35 @@ public function sheet($sheetID, $callback = null) /** * Set csv delimiter + * * @param $delimiter + * * @return $this */ public function setDelimiter($delimiter) { $this->delimiter = $delimiter; + return $this; } /** * Set csv enclosure + * * @param $enclosure + * * @return $this */ public function setEnclosure($enclosure) { $this->enclosure = $enclosure; + return $this; } /** * set selected sheets + * * @param array $sheets */ public function setSelectedSheets($sheets) @@ -308,6 +344,7 @@ public function setSelectedSheets($sheets) /** * Check if sheets were selected + * * @return integer */ public function sheetsSelected() @@ -317,20 +354,26 @@ public function sheetsSelected() /** * Check if the file was selected by index + * * @param $index + * * @return boolean */ public function isSelectedByIndex($index) { $selectedSheets = $this->getSelectedSheetIndices(); - if (empty($selectedSheets)) return true; + if (empty($selectedSheets)) { + return true; + } return in_array($index, $selectedSheets) ? true : false; } /** * Set the selected sheet indices + * * @param $sheets + * * @return $this */ public function setSelectedSheetIndices($sheets) @@ -342,6 +385,7 @@ public function setSelectedSheetIndices($sheets) /** * Return the selected sheets + * * @return array */ public function getSelectedSheetIndices() @@ -351,12 +395,14 @@ public function getSelectedSheetIndices() /** * Remember the results for x minutes + * * @param integer $minutes + * * @return LaravelExcelReader */ public function remember($minutes) { - $this->remembered = true; + $this->remembered = true; $this->cacheMinutes = $minutes; return $this; @@ -364,8 +410,10 @@ public function remember($minutes) /** * Read the file through a config file + * * @param string $config * @param callback|null $callback + * * @return SheetCollection */ public function byConfig($config, $callback = null) @@ -377,7 +425,9 @@ public function byConfig($config, $callback = null) /** * Take x rows + * * @param integer $amount + * * @return LaravelExcelReader */ public function take($amount) @@ -390,7 +440,9 @@ public function take($amount) /** * Skip x rows + * * @param integer $amount + * * @return LaravelExcelReader */ public function skip($amount) @@ -403,8 +455,10 @@ public function skip($amount) /** * Limit the results by x + * * @param integer $take * @param integer $skip + * * @return LaravelExcelReader */ public function limit($take, $skip = 0) @@ -420,10 +474,12 @@ public function limit($take, $skip = 0) /** * Select certain columns + * * @param array $columns + * * @return LaravelExcelReader */ - public function select($columns = array()) + public function select($columns = []) { $this->columns = array_merge($this->columns, $columns); @@ -432,43 +488,45 @@ public function select($columns = array()) /** * Return all sheets/rows + * * @param array $columns + * * @return LaravelExcelReader */ - public function all($columns = array()) + public function all($columns = []) { return $this->get($columns); } /** * Get first row/sheet only + * * @param array $columns + * * @return SheetCollection|RowCollection */ - public function first($columns = array()) + public function first($columns = []) { return $this->take(1)->get($columns)->first(); } /** * Get all sheets/rows + * * @param array $columns + * * @return SheetCollection|RowCollection */ - public function get($columns = array()) + public function get($columns = []) { - if ($this->remembered) - { + if ($this->remembered) { // Return cached results - return Cache::remember(md5($this->file), $this->cacheMinutes, function () use (&$columns) - { + return Cache::remember(md5($this->file), $this->cacheMinutes, function () use (&$columns) { $this->_parseFile($columns); return $this->parsed; }); - } - else - { + } else { // return parsed file $this->_parseFile($columns); @@ -477,50 +535,41 @@ public function get($columns = array()) } /** - * Parse the file in chunks - * @param int $size - * @param $callback - * @throws \Exception - * @return void + * Parse the file in chunks and queues the processing of each chunk + * + * @param int $size + * @param callable $callback + * @param bool $shouldQueue */ - public function chunk($size = 10, $callback = null) + public function chunk($size = 10, callable $callback, $shouldQueue = true) { - // Check if the chunk filter has been enabled - if(!in_array('chunk', $this->filters['enabled'])) - throw new \Exception("The chunk filter is not enabled, do so with ->filter('chunk')"); - // Get total rows $totalRows = $this->getTotalRowsOfFile(); - // Only read - $this->reader->setReadDataOnly(true); - $break = false; - // Start the chunking - for ($startRow = 0; $startRow < $totalRows; $startRow += $chunkSize) - { + for ($startRow = 0; $startRow < $totalRows; $startRow += $chunkSize) { + // Set start index $startIndex = ($startRow == 0) ? $startRow : $startRow - 1; - $chunkSize = ($startRow == 0)? $size + 1 : $size; - - // Set the rows for the chunking - $this->filter->setRows($startRow, $chunkSize); - - // Load file with chunk filter enabled - $this->excel = $this->reader->load($this->file); - - // Slice the results - $results = $this->get()->slice($startIndex, $chunkSize); - - // Do a callback - if(is_callable($callback)) { - $break = call_user_func($callback, $results); + $chunkSize = ($startRow == 0) ? $size + 1 : $size; + + $job = new ChunkedReadJob( + $this->file, + $this->reader->getLoadSheetsOnly(), + $startRow, + $startIndex, + $chunkSize, + $callback, + $shouldQueue + ); + + if ($shouldQueue) { + $this->dispatch($job); + } else { + $break = $job->handle(); } - $this->_reset(); - unset($this->excel, $results); - if ($break === true) { break; } @@ -529,7 +578,9 @@ public function chunk($size = 10, $callback = null) /** * Each + * * @param callback $callback + * * @return SheetCollection|RowCollection */ public function each($callback) @@ -539,31 +590,37 @@ public function each($callback) /** * Parse the file to an array. + * * @param array $columns + * * @return array */ - public function toArray($columns = array()) + public function toArray($columns = []) { - return (array) $this->get($columns)->toArray(); + return (array)$this->get($columns)->toArray(); } /** * Parse the file to an object. + * * @param array $columns + * * @return SheetCollection|RowCollection */ - public function toObject($columns = array()) + public function toObject($columns = []) { return $this->get($columns); } /** * Dump the parsed file to a readable array + * * @param array $columns * @param boolean $die + * * @return string */ - public function dump($columns = array(), $die = false) + public function dump($columns = [], $die = false) { echo '
';
         $die ? dd($this->get($columns)) : var_dump($this->get($columns));
@@ -572,35 +629,40 @@ public function dump($columns = array(), $die = false)
 
     /**
      * Die and dump
+     *
      * @param array $columns
+     *
      * @return string
      */
-    public function dd($columns = array())
+    public function dd($columns = [])
     {
         return $this->dump($columns, true);
     }
 
     /**
      * Init the loading
+     *
      * @param      $file
      * @param bool $encoding
      * @param bool $noBasePath
      */
-    protected function _init($file, $encoding = false, $noBasePath = false)
+    public function _init($file, $encoding = false, $noBasePath = false)
     {
         // Set the extension
         $this->_setFile($file, $noBasePath)
-              ->setExtension()
-              ->setTitle()
-              ->_setFormat()
-              ->_setReader()
-              ->_enableFilters()
-              ->_setInputEncoding($encoding);
+             ->setExtension()
+             ->setTitle()
+             ->_setFormat()
+             ->_setReader()
+             ->_enableFilters()
+             ->_setInputEncoding($encoding);
     }
 
     /**
      * Inject the excel object
+     *
      * @param  PHPExcel $excel
+     *
      * @return void
      */
     public function injectExcel($excel)
@@ -611,32 +673,40 @@ public function injectExcel($excel)
 
     /**
      * Set filters
-     * @param array $filters
      *
+     * @param array $filters
      */
-    public function setFilters($filters = array())
+    public function setFilters($filters = [])
     {
         $this->filters = $filters;
     }
 
+    /**
+     * @return PHPExcel_Reader_IReadFilter
+     */
+    public function getFilter()
+    {
+        return $this->filter;
+    }
+
     /**
      * Enable filters
+     *
      * @return $this
      */
     protected function _enableFilters()
     {
         // Loop through the registered filters
-        foreach($this->filters['registered'] as $key => $class)
-        {
+        foreach ($this->filters['registered'] as $key => $class) {
             // Set the filter inside the reader when enabled and the class exists
-            if(in_array($key, $this->filters['enabled']) && class_exists($class))
-            {
+            if (in_array($key, $this->filters['enabled']) && class_exists($class)) {
                 // init new filter (and overrule the current)
                 $this->filter = new $class;
 
                 // Set default rows
-                if(method_exists($this->filter, 'setRows'))
+                if (method_exists($this->filter, 'setRows')) {
                     $this->filter->setRows(0, 1);
+                }
 
                 // Set the read filter
                 $this->reader->setReadFilter($this->filter);
@@ -648,15 +718,18 @@ protected function _enableFilters()
 
     /**
      * Set the file
+     *
      * @param string $file
      * @param bool   $noBasePath
+     *
      * @return $this
      */
     protected function _setFile($file, $noBasePath = false)
     {
         // check if we have a correct path
-        if (!$noBasePath && !realpath($file))
+        if (!$noBasePath && !realpath($file)) {
             $file = base_path($file);
+        }
 
         $this->file = $file;
 
@@ -665,7 +738,9 @@ protected function _setFile($file, $noBasePath = false)
 
     /**
      * Set the spreadsheet title
+     *
      * @param string|boolean $title
+     *
      * @return LaraveExcelReader
      */
     public function setTitle($title = false)
@@ -677,7 +752,9 @@ public function setTitle($title = false)
 
     /**
      * Set extension
+     *
      * @param string|boolean $ext
+     *
      * @return LaraveExcelReader
      */
     public function setExtension($ext = false)
@@ -689,7 +766,9 @@ public function setExtension($ext = false)
 
     /**
      * Set custom value binder
+     *
      * @param string|boolean $ext
+     *
      * @return void
      */
     public function setValueBinder(PHPExcel_Cell_IValueBinder $binder)
@@ -701,6 +780,7 @@ public function setValueBinder(PHPExcel_Cell_IValueBinder $binder)
 
     /**
      * Reset the value binder back to default
+     *
      * @return void
      */
     public function resetValueBinder()
@@ -712,21 +792,25 @@ public function resetValueBinder()
 
     /**
      * Set the date format
+     *
      * @param bool|string $format The date format
+     *
      * @return LaraveExcelReader
      */
     public function setDateFormat($format = false)
     {
         $this->formatDates = $format ? true : false;
-        $this->dateFormat = $format;
+        $this->dateFormat  = $format;
 
         return $this;
     }
 
     /**
      * Enable/disable date formating
+     *
      * @param  boolean $boolean True/false
      * @param  boolean $format
+     *
      * @return LaraveExcelReader
      */
     public function formatDates($boolean = true, $format = false)
@@ -739,12 +823,13 @@ public function formatDates($boolean = true, $format = false)
 
     /**
      * Set the date columns
+     *
      * @return LaraveExcelReader
      */
     public function setDateColumns()
     {
         $this->formatDates = true;
-        $columns = func_get_args();
+        $columns           = func_get_args();
         $this->dateColumns = array_merge($this->dateColumns, array_flatten($columns));
 
         return $this;
@@ -752,7 +837,9 @@ public function setDateColumns()
 
     /**
      * If the file has a table heading or not
+     *
      * @param  boolean $boolean
+     *
      * @return LaraveExcelReader
      */
     public function noHeading($boolean = true)
@@ -764,7 +851,9 @@ public function noHeading($boolean = true)
 
     /**
      * Set the cell name word separator
+     *
      * @param string $separator
+     *
      * @return LaraveExcelReader
      */
     public function setSeparator($separator)
@@ -776,7 +865,9 @@ public function setSeparator($separator)
 
     /**
      * Spelling mistake backwards compatibility
+     *
      * @param  $separator
+     *
      * @return \Maatwebsite\Excel\Readers\LaraveExcelReader
      */
     public function setSeperator($separator)
@@ -786,7 +877,9 @@ public function setSeperator($separator)
 
     /**
      *  Set default calculate
+     *
      * @param bool $boolean Calculate yes or no
+     *
      * @return LaraveExcelReader
      */
     public function calculate($boolean = true)
@@ -798,7 +891,9 @@ public function calculate($boolean = true)
 
     /**
      * Ignore empty cells
+     *
      * @param  boolean $boolean
+     *
      * @return LaraveExcelReader
      */
     public function ignoreEmpty($boolean = true)
@@ -810,12 +905,12 @@ public function ignoreEmpty($boolean = true)
 
     /**
      * Check if the file has een heading
+     *
      * @return boolean
      */
     public function hasHeading()
     {
-        if (!$this->noHeading)
-        {
+        if (!$this->noHeading) {
             $config = Config::get('excel.import.heading', true);
 
             return $config !== false && $config !== 'numeric';
@@ -826,18 +921,21 @@ public function hasHeading()
 
     /**
      * Get the separator
+     *
      * @return string
      */
     public function getSeparator()
     {
-        if ($this->separator)
+        if ($this->separator) {
             return $this->separator;
+        }
 
         return Config::get('excel.import.separator', Config::get('excel.import.seperator', '_'));
     }
 
     /**
      * Get the dateFormat
+     *
      * @return string
      */
     public function getDateFormat()
@@ -847,6 +945,7 @@ public function getDateFormat()
 
     /**
      * Get the date columns
+     *
      * @return array
      */
     public function getDateColumns()
@@ -856,6 +955,7 @@ public function getDateColumns()
 
     /**
      * Check if we need to calculate the formula inside the cell
+     *
      * @return boolean
      */
     public function needsCalculation()
@@ -865,6 +965,7 @@ public function needsCalculation()
 
     /**
      * Check if we need to ignore the empty cells
+     *
      * @return boolean
      */
     public function needsIgnoreEmpty()
@@ -874,6 +975,7 @@ public function needsIgnoreEmpty()
 
     /**
      * Check if we need to format the dates
+     *
      * @return boolean
      */
     public function needsDateFormatting()
@@ -883,6 +985,7 @@ public function needsDateFormatting()
 
     /**
      * Return the amount of rows to skip
+     *
      * @return integer
      */
     public function getSkip()
@@ -892,6 +995,7 @@ public function getSkip()
 
     /**
      * Return the amount of rows to take
+     *
      * @return integer
      */
     public function getLimit()
@@ -901,6 +1005,7 @@ public function getLimit()
 
     /**
      * Get total rows of file
+     *
      * @return integer
      */
     public function getTotalRowsOfFile()
@@ -914,6 +1019,7 @@ public function getTotalRowsOfFile()
 
     /**
      * Get sheet info for active sheet
+     *
      * @return mixed
      */
     public function getSheetInfoForActive()
@@ -921,11 +1027,11 @@ public function getSheetInfoForActive()
         $spreadsheetInfo = $this->reader->listWorksheetInfo($this->file);
 
         // Loop through the info
-        foreach($spreadsheetInfo as $key => $value)
-        {
+        foreach ($spreadsheetInfo as $key => $value) {
             // When we hit the right worksheet
-            if($value['worksheetName'] == $this->getActiveSheet()->getTitle())
+            if ($value['worksheetName'] == $this->getActiveSheet()->getTitle()) {
                 $index = $key;
+            }
         }
 
         // return total rows
@@ -944,14 +1050,17 @@ protected function initClonedExcelObject($clone)
 
     /**
      * Get the sheet by id or name, else get the active sheet
+     *
      * @param callable|integer|string $sheetID
+     *
      * @return \PHPExcel_Worksheet
      */
     protected function getSheetByIdOrName($sheetID)
     {
         // If is a string, return the sheet by name
-        if(is_string($sheetID))
+        if (is_string($sheetID)) {
             return $this->excel->getSheetByName($sheetID);
+        }
 
         // Else it should be the sheet index
         return $this->excel->getSheet($sheetID);
@@ -959,6 +1068,7 @@ protected function getSheetByIdOrName($sheetID)
 
     /**
      * Get the file title
+     *
      * @return string
      */
     public function getTitle()
@@ -968,6 +1078,7 @@ public function getTitle()
 
     /**
      * Get the current filename
+     *
      * @return mixed
      */
     public function getFileName()
@@ -977,12 +1088,15 @@ public function getFileName()
 
     /**
      * Check if the writer has the called method
+     *
      * @param $method
+     *
      * @return bool
      */
     protected function writerHasMethod($method)
     {
         $this->initNewWriterWhenNeeded();
+
         return method_exists($this->writer, $method) ? true : false;
     }
 
@@ -991,8 +1105,7 @@ protected function writerHasMethod($method)
      */
     protected function initNewWriterWhenNeeded()
     {
-        if(!$this->writer)
-        {
+        if (!$this->writer) {
             $this->writer = app('excel.writer');
             $this->writer->injectExcel($this->excel, false);
             $this->writer->setFileName($this->getFileName());
@@ -1002,6 +1115,7 @@ protected function initNewWriterWhenNeeded()
 
     /**
      * Set the write format
+     *
      * @return LaraveExcelReader
      */
     protected function _setFormat()
@@ -1013,21 +1127,24 @@ protected function _setFormat()
 
     /**
      * Parse the file
+     *
      * @param  array $columns
+     *
      * @return void
      */
-    protected function _parseFile($columns = array())
+    protected function _parseFile($columns = [])
     {
         // Merge the selected columns
         $columns = array_merge($this->columns, $columns);
 
         // Parse the file
-        $parser = new ExcelParser($this);
+        $parser       = new ExcelParser($this);
         $this->parsed = $parser->parseFile($columns);
     }
 
     /**
      * Set the writer
+     *
      * @return LaraveExcelReader
      */
     protected function _setReader()
@@ -1041,13 +1158,14 @@ protected function _setReader()
 
     /**
      * Set the input encoding
+     *
      * @param boolean $encoding
+     *
      * @return LaraveExcelReader
      */
     protected function _setInputEncoding($encoding = false)
     {
-        if ($this->format == 'CSV')
-        {
+        if ($this->format == 'CSV') {
             // If no encoding was given, use the config value
             $encoding = $encoding ? $encoding : Config::get('excel.import.encoding.input', 'UTF-8');
             $this->reader->setInputEncoding($encoding);
@@ -1058,24 +1176,25 @@ protected function _setInputEncoding($encoding = false)
 
     /**
      * Set reader defaults
+     *
      * @return void
      */
     protected function _setReaderDefaults()
     {
         // Set CSV delimiter
-        if ($this->format == 'CSV')
-        {
+        if ($this->format == 'CSV') {
             // If no delimiter was given, take from config
-            if(!$this->delimiter)
+            if (!$this->delimiter) {
                 $this->reader->setDelimiter(Config::get('excel.csv.delimiter', ','));
-            else
+            } else {
                 $this->reader->setDelimiter($this->delimiter);
+            }
 
-            if(!$this->enclosure)
+            if (!$this->enclosure) {
                 $this->reader->setEnclosure(Config::get('excel.csv.enclosure', '"'));
-            else
+            } else {
                 $this->reader->setEnclosure($this->enclosure);
-
+            }
         }
 
         // Set default calculate
@@ -1091,7 +1210,7 @@ protected function _setReaderDefaults()
         $this->formatDates = Config::get('excel.import.dates.enabled', true);
 
         // Set default date columns
-        $this->dateColumns = Config::get('excel.import.dates.columns', array());
+        $this->dateColumns = Config::get('excel.import.dates.columns', []);
 
         // Set default include charts
         $this->reader->setIncludeCharts(Config::get('excel.import.includeCharts', false));
@@ -1099,9 +1218,10 @@ protected function _setReaderDefaults()
 
     /**
      * Reset the writer
+     *
      * @return void
      */
-    protected function _reset()
+    public function _reset()
     {
         $this->excel->disconnectWorksheets();
         $this->resetValueBinder();
@@ -1110,6 +1230,7 @@ protected function _reset()
 
     /**
      * Get excel object
+     *
      * @return PHPExcel
      */
     public function getExcel()
@@ -1119,29 +1240,24 @@ public function getExcel()
 
     /**
      * Dynamically call methods
+     *
      * @param  string $method
      * @param  array  $params
+     *
      * @throws LaravelExcelException
      */
     public function __call($method, $params)
     {
         // Call a php excel method
-        if (method_exists($this->excel, $method))
-        {
+        if (method_exists($this->excel, $method)) {
             // Call the method from the excel object with the given params
-            return call_user_func_array(array($this->excel, $method), $params);
-        }
-
-        // If it's a reader method
-        elseif (method_exists($this->reader, $method))
-        {
+            return call_user_func_array([$this->excel, $method], $params);
+        } // If it's a reader method
+        elseif (method_exists($this->reader, $method)) {
             // Call the method from the excel object with the given params
-            return call_user_func_array(array($this->reader, $method), $params);
-        }
-
-        elseif($this->writerHasMethod($method))
-        {
-            return call_user_func_array(array($this->writer, $method), $params);
+            return call_user_func_array([$this->reader, $method], $params);
+        } elseif ($this->writerHasMethod($method)) {
+            return call_user_func_array([$this->writer, $method], $params);
         }
 
         throw new LaravelExcelException('[ERROR] Reader method [' . $method . '] does not exist.');
diff --git a/tests/Filters/ChunkReadFilterTest.php b/tests/Filters/ChunkReadFilterTest.php
index 7d227e0fc..0c4b4c650 100644
--- a/tests/Filters/ChunkReadFilterTest.php
+++ b/tests/Filters/ChunkReadFilterTest.php
@@ -2,7 +2,8 @@
 
 use Mockery as m;
 
-class ChunkReadFilterTest extends TestCase {
+class ChunkReadFilterTest extends TestCase
+{
 
     public function setUp()
     {
@@ -10,102 +11,93 @@ public function setUp()
         $this->excel = app('excel');
     }
 
-
     public function testCanChunkXls()
     {
-        $this->assertCanChunkIntoGroups(1,"sample.xls",20);
-        $this->assertCanChunkIntoGroups(1,"sample.xls",15);
-        $this->assertCanChunkIntoGroups(2,"sample.xls",10);
-        $this->assertCanChunkIntoGroups(3,"sample.xls",5);
-        $this->assertCanChunkIntoGroups(15,"sample.xls",1);
+        $this->assertCanChunkIntoGroups(1, "sample.xls", 20);
+        $this->assertCanChunkIntoGroups(1, "sample.xls", 15);
+        $this->assertCanChunkIntoGroups(2, "sample.xls", 10);
+        $this->assertCanChunkIntoGroups(3, "sample.xls", 5);
+        $this->assertCanChunkIntoGroups(15, "sample.xls", 1);
     }
 
-
     public function testCanChunkXlsx()
     {
-    	$this->assertCanChunkIntoGroups(1,"sample.xlsx",20);
-    	$this->assertCanChunkIntoGroups(1,"sample.xlsx",15);
-    	$this->assertCanChunkIntoGroups(2,"sample.xlsx",10);
-        $this->assertCanChunkIntoGroups(3,"sample.xlsx",5);
-    	$this->assertCanChunkIntoGroups(15,"sample.xlsx",1);
+        $this->assertCanChunkIntoGroups(1, "sample.xlsx", 20);
+        $this->assertCanChunkIntoGroups(1, "sample.xlsx", 15);
+        $this->assertCanChunkIntoGroups(2, "sample.xlsx", 10);
+        $this->assertCanChunkIntoGroups(3, "sample.xlsx", 5);
+        $this->assertCanChunkIntoGroups(15, "sample.xlsx", 1);
     }
 
-
     public function testCanChunkCsv()
     {
-    	$this->assertCanChunkIntoGroups(1,"sample.csv", 20);
-    	$this->assertCanChunkIntoGroups(1,"sample.csv", 15);
-    	$this->assertCanChunkIntoGroups(2,"sample.csv", 10);
-        $this->assertCanChunkIntoGroups(3,"sample.csv", 5);
-    	$this->assertCanChunkIntoGroups(15,"sample.csv", 1);
-    }
-
-
-    public function testCanStopChunkEarly()
-    {
-        $this->assertCanStopChunkEarly(1,"sample.xls", 5);
-        $this->assertCanStopChunkEarly(1,"sample.xlsx", 2);
-        $this->assertCanStopChunkEarly(2,"sample.csv", 1);
+        $this->assertCanChunkIntoGroups(1, "sample.csv", 20);
+        $this->assertCanChunkIntoGroups(1, "sample.csv", 15);
+        $this->assertCanChunkIntoGroups(2, "sample.csv", 10);
+        $this->assertCanChunkIntoGroups(3, "sample.csv", 5);
+        $this->assertCanChunkIntoGroups(15, "sample.csv", 1);
     }
 
-
     public function testCanChunkMultipleSheets()
     {
-        $output = [];
-
-        $rounds = 0;
+        file_put_contents(__DIR__ . '/log.txt', '');
+        file_put_contents(__DIR__ . '/rounds.txt', '');
 
         // test with small chunks
-        $chunk_size = 2;
-        $expected = "1,3,5,7,9,11,13,15,17,19,1,3,5,7,9,11,13,15,17,19";
+        $chunk_size      = 2;
+        $expected        = "1,3,5,7,9,11,13,15,17,19,1,3,5,7,9,11,13,15,17,19";
         $expected_chunks = 10;
 
         // Sheet2 has more rows than sheet 1
-        $this->excel->filter('chunk')->selectSheets('Sheet2')->load(__DIR__ . "/files/multi.xls")->chunk($chunk_size,function($results) use (&$output, &$rounds){
-            $rounds++;
-            foreach ($results as $row) {
-                $output[] = (int) $row->header;
-            }
-        });
-
-        $this->assertEquals($expected, implode(",", $output ), "Chunked ($chunk_size) value not equal with source data.");
-        $this->assertEquals($expected_chunks, $rounds, "Expecting total chunks is $expected_chunks when chunk with size $chunk_size");
-
+        $this->excel->filter('chunk')
+                    ->selectSheets('Sheet2')
+                    ->load(__DIR__ . "/files/multi.xls")
+                    ->chunk($chunk_size, function ($results) {
+                        foreach ($results as $row) {
+                            $output[] = (int)$row->header;
+                        }
+
+                        $previous = file_get_contents(__DIR__ . '/log.txt');
+                        $previous = !empty($previous) ? $previous . ',' : $previous;
+                        $rounds   = file_get_contents(__DIR__ . '/rounds.txt');
+
+                        file_put_contents(__DIR__ . '/log.txt', $previous . implode(',', $output));
+                        file_put_contents(__DIR__ . '/rounds.txt', $rounds . '+');
+                    }, false);
+
+        $output = file_get_contents(__DIR__ . '/log.txt');
+        $rounds = strlen(file_get_contents(__DIR__ . '/rounds.txt'));
+
+        $this->assertEquals($expected, $output, "Chunked ($chunk_size) value not equal with source data.");
+        $this->assertEquals($expected_chunks, $rounds,
+            "Expecting total chunks is $expected_chunks when chunk with size $chunk_size");
     }
 
-
     private function assertCanChunkIntoGroups($expected_chunks, $file, $chunk_size)
     {
-    	$output = [];
-        
-        $rounds = 0;
-
-        $this->excel->filter('chunk')->load(__DIR__ . "/files/{$file}")->chunk($chunk_size,function($results) use (&$output, &$rounds){
-        	$rounds++;
-            foreach ($results as $row) {
-        		$output[] = (int) $row->header;
-        	}
-        });
-
-        $expected = "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15";
+        file_put_contents(__DIR__ . '/log.txt', '');
+        file_put_contents(__DIR__ . '/rounds.txt', '');
 
-        $this->assertEquals($expected, implode(",", $output ), "Chunked ($chunk_size) value not equal with source data.");
-        $this->assertEquals($expected_chunks, $rounds, "Expecting total chunks is $expected_chunks when chunk with size $chunk_size");
+        $this->excel->filter('chunk')->load(__DIR__ . "/files/{$file}")->chunk($chunk_size, function ($results) {
 
-    }
+            foreach ($results as $row) {
+                $output[] = (int)$row->header;
+            }
 
-    private function assertCanStopChunkEarly($expected_chunks, $file, $chunk_size)
-    {
-        $rounds = 0;
+            $previous = file_get_contents(__DIR__ . '/log.txt');
+            $previous = !empty($previous) ? $previous . ',' : $previous;
+            $rounds   = file_get_contents(__DIR__ . '/rounds.txt');
 
-        $this->excel->filter('chunk')->load(__DIR__ . "/files/{$file}")->chunk($chunk_size,function($results) use (&$rounds, $expected_chunks){
-            $rounds++;
+            file_put_contents(__DIR__ . '/log.txt', $previous . implode(',', $output));
+            file_put_contents(__DIR__ . '/rounds.txt', $rounds . '+');
+        }, false);
 
-            if ($rounds === $expected_chunks) {
-                return true;
-            }
-        });
+        $output   = file_get_contents(__DIR__ . '/log.txt');
+        $rounds   = strlen(file_get_contents(__DIR__ . '/rounds.txt'));
+        $expected = "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15";
 
-        $this->assertEquals($expected_chunks, $rounds, "Expecting total chunks is $expected_chunks when chunk with size $chunk_size");
+        $this->assertEquals($expected, $output, "Chunked ($chunk_size) value not equal with source data.");
+        $this->assertEquals($expected_chunks, $rounds,
+            "Expecting total chunks is $expected_chunks when chunk with size $chunk_size");
     }
 }
diff --git a/tests/Filters/log.txt b/tests/Filters/log.txt
new file mode 100644
index 000000000..620ec9275
--- /dev/null
+++ b/tests/Filters/log.txt
@@ -0,0 +1 @@
+1,3,5,7,9,11,13,15,17,19,1,3,5,7,9,11,13,15,17,19
\ No newline at end of file
diff --git a/tests/Filters/rounds.txt b/tests/Filters/rounds.txt
new file mode 100644
index 000000000..79f6d4445
--- /dev/null
+++ b/tests/Filters/rounds.txt
@@ -0,0 +1 @@
+++++++++++
\ No newline at end of file
diff --git a/tests/TestCase.php b/tests/TestCase.php
index 438105bb7..25d0c8389 100644
--- a/tests/TestCase.php
+++ b/tests/TestCase.php
@@ -28,5 +28,4 @@ protected function getPackagePath()
             'Excel'
         )));
     }
-
-}
\ No newline at end of file
+}