Compound interest in PHP part 1
Created:20 Sep 2017 00:25:21 , in Maths
In this article I look at how to write a reusable piece of code for finding compound interest and total accumulated value of deposit or loan in PHP programming language. Code developed here will become a basis to both a command line interface script and website form then.
Since coding a CLI script frequently involves processing command line arguments, validating user input, handling errors and outputting formatted result, this series of two articles is a great opportunity to look closer at PHP getopt and I/O stream functions among other things.
Compound interest in a nutshell
Compound interest is interest on previously-accumulated interest. This, can be seen clearly when principal sum is set to 1.
In simple terms, the interest from the previous period is reinvested till the last period of a loan or a deposit is reached. Nothing gets withdrawn in the meantime.
Total accumulated value is the principal sum plus accumulated interest at the end of the last period of loan or deposit.
Compound interest in PHP
For finding compound interest I have written a PHP class called SWWWCompoundInterest. The class uses a not-too-complicated formula to find required value. Since the formula depends on a few, possibly user-supplied variables, the class prepares and validates user input. If configured to do that, it will format the computation result as well.
SWWWCompoundInterest has some public methods:
get - returns compound interest calculation result,
valid - returns whether user-provided configuration options are valid,
error - returns errors if one or more configuration options have been found to be incorrect,
inspect - allows to checking values of private properties of an SWWWCompoundInterest class object.
Here is the code:
/*
Class: SWWWCompoundInterest
Description: Find compound interest or total accumulated value
Author: Sylwester Wojnowski
WWW: wojnowski.net.pl
Methods:
get - public - compute compound interest or total accumulated value
return - string or array - compound interest or total accumulated value for one or more years
valid - public - check whether user input is valid
return - bool
error($key) - public - get error messages for one or more pieces of user input
parameters:
$key - string - property name
return - array - error messages
inspect($property) - public - inspect private properties of the object of SWWWCompoundInterest
parameters:
[$key] - string - index in $input array
return - mixed - SWWWCompoundInterest object property value
*/
class SWWWCompoundInterest{
private $valid = true;
private $error = null;
// input option labels
private $principal = 'p';
private $rate = 'r';
private $frequency = 'f';
private $years = 'y';
private $sums = 'n';
private $interest = 'g';
private $format = 't';
// defaults
protected $defs = array(
'principal' => CompoundInterestValid::PRINCIPAL,
'rate' => CompoundInterestValid::RATE,
'frequency' => CompoundInterestValid::FREQUENCY,
'years' => CompoundInterestValid::YEARS,
'sums' => false,
'interest' => false,
'format' => false
);
public function __construct($conf){
$this -> __prep_input($conf);
$this -> __validate_input($this -> defs);
$this -> defs = (object)$this -> defs;
}
// overwrite defaults with user input
private function __prep_input($input){
foreach($input as $opt_name => $opt_value){
switch($opt_name){
case $this -> sums: // sums
$this -> defs['sums'] = true;
break;
case $this -> interest:
$this -> defs['interest'] = true;
break;
case $this -> principal:
$this -> defs['principal'] = (float)$opt_value;
break;
case $this -> rate:
$this -> defs['rate'] = (float)$opt_value;
break;
case $this -> frequency:
$this -> defs['frequency'] = (int)$opt_value;
break;
case $this -> years:
$this -> defs['years'] = (int)$opt_value;
break;
// format
case $this -> format:
$this -> defs['format'] = (string)trim(strip_tags($opt_value));
break;
}
}
return $this -> defs;
}
// validate user input
private function __validate_input($input){
$validation = SWWWValidation::instance($input);
$validation -> rule(
'principal',
array('CompoundInterestValid','validate_principal'),
array(':value'),
CompoundInterestValid::PRINCIPAL_INVALID
);
$validation -> rule(
'rate',
array('CompoundInterestValid','validate_rate'),
array(':value'),
CompoundInterestValid::RATE_INVALID
);
$validation -> rule(
'frequency',
array('CompoundInterestValid','validate_frequency'),
array(':value'),
CompoundInterestValid::FREQUENCY_INVALID
);
$validation -> rule(
'years',
array('CompoundInterestValid','validate_years'),
array(':value'),
CompoundInterestValid::YEARS_INVALID
);
$validation -> rule(
'format',
array('CompoundInterestValid','validate_format'),
array(':value'),
CompoundInterestValid::FORMAT_INVALID
);
if( !$validation -> check() ){
$this -> valid = false;
$this -> error = $validation -> errors();
}
return $this -> defs;
}
// check for errors
public function valid(){ return $this -> valid; }
// get error if any
public function error($key = ''){ return $this -> error; }
// inspect property
public function inspect($property){
return property_exists($this -> defs,$property) ? $this -> defs -> {$property} : false;
}
// get raw data
public function get(){
if( !$this -> valid ){ return false; }
$x = null;
if(!$this -> defs -> sums){
$cpdInterest = CompoundInterestBase::find($this -> defs);
if($this -> defs -> interest){
$x = $cpdInterest - $this -> defs -> principal;
}else{
$x = $cpdInterest;
}
// sums
}else{
$x = array();
$years = $this -> defs -> years;
for($i = 0; $i <= $years; $i++){
$this -> defs -> years = $i;
$cpdInterest = CompoundInterestBase::find($this -> defs);
if($this -> defs -> interest){
$x[] = $cpdInterest - $this -> defs -> principal;
}else{
$x[] = $cpdInterest;
}
}
}
return $this -> defs -> format ? SWWWStringArrayFormat::format($x,$this -> defs -> format) : $x;
}
}
SWWWCompoundInterest dependencies
In order to make SWWWCompoundInterest less complicated and common functionality in it available to implementations of similar problems, some code has been moved to smaller, more specialized classes (They should be included before SWWWCompoundInterest class is used).
The specialized classes are:
CompoundInterestBase, CompoundInterestValid, SWWWValidation, SWWWStringArrayFormatCompound interest formula
CompoundInterestBase consists of just one static method. The method allows to finding compound interest or total accumulated value.
/*
Class: SWWWCompoundInterestBase
Description: Provides basic formula for finding compound interest
Author: Sylwester Wojnowski
WWW: wojnowski.net.pl
Methods:
find($data) - public - find compound interest with a formula
properties:
$data - object - object with variables for the formula
return - double - compound interest
*/
class CompoundInterestBase{
public static function find($data){
return $data -> principal * pow(
(1 + $data -> rate / $data -> frequency),
$data -> frequency * $data -> years
);
}
}
Validating user input
Two PHP classes are used to prepare and validate user input for SWWWCompoundInterest. They are CompoundInterestValid and SWWWValidation. The former, an extension to CompoundInterestBase, contains constants and functions used by the latter. The latter is a validation class I descrbed in detail in article on validating user input with functions.
/*
Class: SWWWCompoundInterestValid
Description: provide validation functions for SWWWValidation
Author: Sylwester Wojnowski
WWW: wojnowski.net.pl
*/
class CompoundInterestValid extends CompoundInterestBase{
const PRINCIPAL = 1.0;
const PRINCIPAL_MAX = 1000000;
const RATE = 0.0;
const RATE_MIN = -10;
const RATE_MAX = 10;
const FREQUENCY = 1;
const FREQUENCY_MAX = 12;
const YEARS = 0;
const YEARS_MAX = 100;
const PRINCIPAL_INVALID = "Specified principal value is incorrect.";
const RATE_INVALID = "Specified rate value is incorrect.";
const FREQUENCY_INVALID = "Specified frequency value is incorrect.";
const YEARS_INVALID = "Specified number of years is incorrect.";
const FORMAT_INVALID = "Specified format string is incorrect.";
/* validation methods */
public static function validate_principal($val){
return $val >= self::PRINCIPAL and $val <= self::PRINCIPAL_MAX;
}
public static function validate_rate($val){
return $val >= self::RATE_MIN and $val <= self::RATE_MAX and $val !== self::RATE;
}
public static function validate_frequency($val){
return $val >= self::FREQUENCY and $val <= self::FREQUENCY_MAX;
}
public static function validate_years($val){
return $val >= self::YEARS and $val <= self::YEARS_MAX;
}
public static function validate_format($val){
if($val === false){ return true; } // default value
return (bool)preg_match('/^%\.[0-9]{0,6}f$/',$val);
}
}
Formatting output
For formatting arrays or strings of numeric values I wrote SWWWStringArrayFormat.
Here is a listing:
/*
Class: SWWWStringArrayFormat
Description: Format array or string according to a format provided
Author: Sylwester Wojnowski
WWW: wojnowski.net.pl
Methods:
format($x,$format) - public static - format variable value(s) according to a format
properties:
$x - float/array - value(s) to format
$data - string - format like described for sprintf PHP function
return - double - compound interest
*/
class SWWWStringArrayFormat{
// format array or string according to a format
public static function format($x,$format){
if(is_array($x)){
$i = count($x);
while($i > 0){
$x[$i - 1] = sprintf($format,$x[$i -1]);
$i--;
}
return $x;
} else {
return sprintf($format,$x);
}
}
}
Configuring SWWWCompoundInterest
SWWWCompoundInterest needs some configuring before it can be used.
The available configuration options are as follows (options in brackets are optional):
p - float - principal sum,
r - float - nominal rate of interest,
f - int - frequency of compounding,
y - int - number of years compound interest is applied
[n] - bool - if not null, total accumulated values for each of y years will be returned
[g] - bool - if not null, compound interest for each of y years will be returned (principal sum is not taken into account in this case)
[t] - string - output format string
Use examples
What follows are some examples of finding compound interest or total accumulated value(s) with SWWWCompoundInterest:
Total accumulated value over 10 years period, with principal value 100, interest rate 0.05 and compounding frequency 1:
$cp = new SWWWCompoundInterest(
array(
'p' => 100, // principal
'r' => 0.05, // rate
'f' => 1, // frequency
'y' => 10 // years
)
);
$cp -> get();
=> 162.88946267774415
Total compound interest accumulated over 10 years period, with principal value 100, interest rate 0.05 and compounding frequency 1:
$cp = new SWWWCompoundInterest(
array(
'p' => 100,
'r' => 0.05,
'f' => 1,
'y' => 5,
'g' => false
)
);
$cp -> get();
=> 27.628156250000018
Total accumulated values for each of 3 years, with principal value 100, interest rate 0.05, compounding frequency 1 and result formatted according to '%.2f' format:
$cp = new SWWWCompoundInterest(
array(
'p' => 100,
'r' => 0.05,
'f' => 1,
'y' => 3,
'n' => true,
't' => '%.2f'
)
);
$cp -> get()
=> array(100.00,105.00,110.25,115.76)
I guess, these examples give enough insight on how SWWWCompoundInterest can be used.
Final thoughts
Compound interest can be found in a couple of lines of code. However, the trouble with the "quick option" is that it is neither reusable nor, since the formula depends on a couple of user-supplied variables, secure. My approach is longer and more complicated but it yields more secure and reusable body of code.
In the second part of this short series on compound interest I'm going to build both a CLI script and a form based on the PHP code introduced in this article.
This post was updated on 04 Nov 2017 11:28:13
Tags: php
Author, Copyright and citation
Author
Author of the this article - Sylwester Wojnowski - is a sWWW web developer. He has been writing computer code for the websites and web applications since 1998.
Copyrights
©Copyright, 2024 Sylwester Wojnowski. This article may not be reproduced or published as a whole or in parts without permission from the author. If you share it, please give author credit and do not remove embedded links.
Computer code, if present in the article, is excluded from the above and licensed under GPLv3.
Citation
Cite this article as:
Wojnowski, Sylwester. "Compound interest in PHP part 1." From sWWW - Code For The Web . https://swww.com.pl//main/index/compound-interest-in-php-part-1
Add Comment