Get user Country Code by IP via php function - php

I have a snippet of code from an existing system that is pulling the users 2-digit country code from an obsolete database table. Normally I have updated the table on a regular basis but I am quite honestly sick of doing it. What I would like to do is make this more of a live pull each time a user logs in. This is the existing code
public static function geoip_country_code_by_name($ip)
{
if ($_SERVER['HTTP_CF_IPCOUNTRY']) {
return $_SERVER['HTTP_CF_IPCOUNTRY'];
}
$sql = "SELECT country FROM ip2country WHERE ip < INET_ATON('$ip') ORDER BY ip DESC LIMIT 0,1";
$res = DB::selectOneBySQL($sql);
return $res->country;
}
I am looking for suggestions on how to best accomplish this.

I decided to use this solution. It also allows me the option to grab additional information in the future if required. I might eventually move to a paid service either with this API or another but for now this solved my issue
public static function geoip_country_code_by_name($ip)
{
if ($_SERVER['HTTP_CF_IPCOUNTRY']) {
return $_SERVER['HTTP_CF_IPCOUNTRY'];
}
$iptolocation = #json_decode(file_get_contents("http://www.geoplugin.net/json.gp?ip=" . $ip));
$creatorlocation = $iptolocation->geoplugin_countryCode;
return $creatorlocation;
}

Related

Logging profile updates in PHP

I have a edit profile page in my social media website.
When users click submit on the form. I run an update query to obviously update the users field in the database.
How can I optimize this scenario to include the logging of which particular fields are updated?
So for e.g.
One scenario could be:
Chris updated his profile picture.
Another scenario would be:
Chris updated his profile, inc:
Email
Username
Address
Address 2
Can anyone offer a solution to this?
I feel there is no need for code as all it is, is an update query.
Thanks
When writing out the form, save the current states in the $_SESSION-variable. The check the submitted forms and compare with the data in the $_SESSION-variable. Then only make an update on the forms that have changed.
if($_SESSION['name'] != $myform['name']) { $sql[] = "name = '{$myform['name']}'"; }
if($_SESSION['img'] != $myform['img']) { $sql[] = "img = '{$myform['img']}'"; }
$sqlstring = "UPDATE mytable SET " . implode(",",$sql);
// run the sql
EDIT: to implement logging:
// populate the variables (name, img) from the db/session with the highest revision number.
// ie SELECT * FROM mytable where userid = $userid ORDER BY revision DESC LIMIT 1
$revision = $_SESSION['revision'] + 1;
$sql = "INSERT INTO mytable SET img = '$img', name='$name', revision='$revision'";
Did you put all that information in a $_SESSION or something? If so, you can unset the session and declare it again, with the new info.
You can use custom setters / getters to do this. Check the code below.
You can also add additional checks to make sure the values have changed, or use magic methods to make things more dynamic.
class MyObject
{
protected $modifiedFields = array();
public function setField($value) {
if (!in_array('field', $this->modifiedFields)) {
$this->modifiedFields[] = 'field';
}
}
}
If you have the modified fields, you can just run the update query to contain only these fields.

phpunit testing ???? how shall i compare to xml files before and after api function call(the function contains the stored procedure)

after searching for a long time got this great article its really very nice
but i am facing a bit problem here in my stuff as u have used direct mysql query in api i have used stored procedure in here and every time i have to compare two XML before and after even for a single short and sweet query so is there any alternative for this process but which is this secure
please chk this out u will get i more clearly
database testing in php using phpunit,simpletest on api haveing stored procedure
or how shall i compare to xml files before and after api function call(the function contains the stored procedure)
means i am able to get the before state with mysql-dump but the after but not getting the instant after xml state
sorry for the English but tried my best
thanks for the help friend
have to write an unit test test for the api function
public function delete($userId)
{
// this function calls a stored procedure
$sql = "CALL Delete_User_Details(:userId)";
try {
$db = parent::getConnection();
$stmt = $db->prepare($sql);
$stmt->bindParam("userId", $userId);
$stmt->execute();
$id = $stmt->fetchObject();
if ($id == null) {
$delete_response->createJSONArray("DATABASE_ERROR",0);
} else {
$delete_response->createJSONArray("SUCCESS",1);
}
} catch (PDOException $e) {
$delete_response->createJSONArray("DATABASE_ERROR",0);
}
return $delete_response->toJSON();
}
i have writen this unit test for it now want to write an dbunit for it
public function testDeleteUser()
{
$decodedResponse = $response->json();
$this->assertEquals($response->getStatusCode(), 200);
$this->assertEquals($decodedResponse['status']['StatusMSG'], 'SUCCESS');
$this->assertEquals($decodedResponse['status']['Code'], '1');
}
help guyss
u can just simply test it before by calling the query like
$sql = "select * from user";
and compare it with BeforeDeleteUser.xml
And the Call Ur stored procedure
$sql = "CALL Delete_User_Details(:userId)";
And for the after case just repeat the before one again
$sql = "select * from user";
and compare it with AfterDeleteUser.xml
see the logic is very simple if u have 5 Users in BeforeDeleteUser.xml and it results true and after the call of CALL Delete_User_Details(:userId) stored procedure , the AfterDeleteUser.xml should contain only 4 user (or maybe idDelete field to 0 that depends on ur implementation)

Limit the database access though PHP

For a plugin system I am writing, I need to write a database API. But I want to restrict database access, so plugins can't see other tables than the ones they created through a specific function. How would I enable plugins to use SQL, but not give them full access at the same time?
Here is some code, it may not be working, but it shows the idea behind it:
class Api_Database {
private $pluginid;
function __construct($pluginid) {
$this->pluginid = $pluginid;
}
function query($sql, $tablename) {
$db = new Sys_Database;
$db->query(str_replace('{table}', $pluginid.$tablename, $sql));
}
}
Am I thinking in the right direction here? How would you create such a system, only more secure?
The idea is to create a table containing the list of created tables linked to the user...
Something like :
privileges
user_id
table_name
And in your query
SELECT FROM privileges WHERE user_id = '{user_id}' and table_name = '{table}';
And after check if the row exist. If yes, the user have the right to use the table!
class Api_Database {
private $pluginid;
function __construct($pluginid) {
$this->pluginid = $pluginid;
}
function query($sql, $tablename) {
$hasPrivilege = $this->checkPrivileges($tablename, $userid);
if(!$hasPrivilege) return false; //for example
$db = new Sys_Database;
$db->query(str_replace('{table}', $pluginid.$tablename, $sql));
}
function checkPrivileges($table, $user_id) {
$db = new Sys_Database;
$result = $db->query('SELECT id FROM privileges WHERE user_id = "'.$user_id.'" AND table_name = "'.$table.'"');
return ($result && $result->num_rows);
}
}
EDIT
So if I understand correctly, you are using a PHP plugin to access SQL data, but you can't or doesn't want to change it.
You cant' add SQL users too, and you wan't to restrict PHP Dev to make some queries in some table via PHP?? Hum... Impossible!
Because to be able to disallow database table access, YOU HAVE TO MANIPULATE PHP OR MYSQL USERS...
Or if I'm wrong, sorry, it's difficult to follow you!

How to email all users with user-specific information.

I've built a contest system for a website, how it works is a user logs in, submits a ballot based on a real life event (sale of a particular object), and then at the end of the month, a random ballot is chosen and the owner of that ballot is the winner.
I've been asked to create a script which will email all users in the database the current amount of ballots they have in the system.
My current login/registration system is a heavily edited version of HTML-Form-Guies Simple PHP Registration System.
I know the pseudo code for what I want to do.
Step by step, the method needed goes like this.
Call on EmailUsersTotalEntries, populates an array with all the users in the database, pick the first entry in the array, user 1, find the sum of the all the rows in the itemsold column with the userid 1. then send user one an email with the results of the select sum(itemsold) from ballots where userid = 1; to user 1. Then the loop goes to user 2 and does the same thing, until it has sent an email to every user in the database.
Here are a few of the methods that I have either written or that are from the login system that will be used to accomplish this. My only problem is I dont know how to make a loop so that it will start from user 1 and then keep going all the way to user 2, and I dont know how to query the database for the user_id of whatever user the database/loop is currently on.
Methods are as follows:
This is the main method, it will call sub methods to collect the users and then send the actual email. I'm not sure if TotalEntries should be an array or not
function EmailTotalEntries()
{
if(empty($_POST['email']))
{
$this->HandleError("Email is empty!");
return false;
}
$user_rec = array();
if(false === $this->GetUsers($user_rec))
{
return false;
}
**/* $TotalEntries = array(); */**
if(false === $this->GetTotalEntriesForEmail($user_rec, $TotalEntries)
{
return false;
}
//At this point, I have an array, user_rec, populated with all the data from my users table, and an array $TotalEntries that will have nothing since its trying to pull from user_rec, which usually is one user but right now is all of the users.
//This is where I know I should have already started the loop, so chosen the first element in user_rec, and applied the GetTotalEntriesForEmail method, then the SendUserEmail method, then gone to the top of the loop and gone to the second user_rec element and repeat.
if(false === $this->SendUsersEmail($user_rec, $TotalEntries))
{
return false;
}
return true;
}
This is the method that collects the users
function GetUsers(&$user_rec)
{
if(!$this->DBLogin())
{
$this->HandleError("Database login failed!");
return false;
}
$result = mysql_query("Select * from $this->tablename",$this->connection);
$user_rec = mysql_fetch_assoc($result);
return true;
}
Here is the method I wrote to get the TotalEntries for a user that is logged in (checking his control panel to see how many entries he has)
function GetTotalEntries()
{
if(!$this->CheckLogin())
{
$this->HandleError("Not logged in!");
return false;
}
$user_rec = array();
if(!$this->GetUserFromEmail($this->UserEmail(),$user_rec))
{
return false;
}
$qry = "SELECT SUM(itemsold) AS TotalEntries FROM entries WHERE user_id = '".$user_rec['id_user']."'";
$result = mysql_query($qry,$this->connection);
while($row = mysql_fetch_array($result))
{
echo $row['TotalEntries'];
}
}
And here is how I believe it needs to be adapted to work in the email.
function GetTotalEntriesForEmail($user_rec, &$TotalEntries)
{
if(!$this->DBLogin())
{
$this->HandleError("Database login failed!");
return false;
}
$qry = "SELECT SUM(itemsold) FROM entries WHERE user_id = '".$user_rec['id_user']."'"; //$user_rec['id_user'] should the be id of the user the loop is currently on.
$result = mysql_query($qry,$this->connection);
$TotalEntries = mysql_fetch_assoc($result);
return true;
}
Heres the actual email
function SendUsers($user_rec, $TotalBallots)
{
$email = $user_rec['email']; //should be the for the user the loop is currently on.
$mailer = new PHPMailer();
$mailer->CharSet = 'utf-8';
$mailer->AddAddress($email,$user_rec['name']); //user the loop is currently on.
$mailer->Subject = "Total Ballots to Date";
$mailer->From = $this->GetFromAddress();
$mailer->Body ="Hello ".$user_rec['name']."\r\n\r\n". //Same thing
"To date you have: "/* .$TotalBallots. */" ballots.\r\n" //Same thing
if(!$mailer->Send())
{
return false;
}
return true;
}
I'm not very good at PHP, and this whole thing is a learning experience for me, so help is greatly appreciated.
If I havent been clear, maybe giving an example in another language would be clearer, so heres what I want to do, but in java
for(x = 0; x <= user_rec.length; x++)
{
int ballots = getTotalEntriesForUser(x);
sendEmailToUser(ballots)
}
If I havent been clear enough, please let me know and I will try to clarify as best as possible.
How can I combine the above code with a loop that will send all users an email, one by one, each email unique to the user it is sent to?
Are your functions part of a class? You wouldn't necessarily need them to do this. Here's my recommendation, which you can turn into functions, or a class, if you want. Also, you may want to consider looking into, and using MySQLi, and taking advantage of the classes it uses. Again, all just my recommendations.
Without knowing your table structure, I'm just taking a guess at this.
$sql = mysql_query("SELECT u.*,
u.user_id AS user,
COALESCE(SUM(e.itemssold), 0) AS total_items
FROM users u
LEFT JOIN entries e ON e.user_id = u.user_id
GROUP BY u.user_id");
while($row = mysql_fetch_array($sql))
{
$user = $row['user'];
$email = $row['user_email'];
$items = $row['total_items'];
yourEmailFunction($email, $items);
}
This pulls information from your users table, and your entries table based on matching User ID's. It sets the User ID from the user table as user so you don't have to try and distinguish between the two later. To learn about the COALESCE function, read here. The while() function will loop through every user it pulls from that SQL statement.
This hasn't been tested in any way, but that's basically what you need. Just pass the User's email, and the total Items, and write your email function to send that info to that email address.
However, if you know your functions work properly, and want to use a for loop, such as the one you provided in Java, here's how you'd write it in PHP.
for($x = 0; $x <= count($user_rec); $x++)
{
$ballots = getTotalEntriesForUser($x);
sendEmailToUser($ballots);
}

Can I get feedback on this PHP function that tests if a user has signed up?

I'm just getting started on writing functions instead of writing everything inline. Is this how a reusable function is typically written?
function test_user($user) {
$conn = get_db_conn();
$res = mysql_query("SELECT * FROM users WHERE uid = $user");
$row = mysql_fetch_assoc($res);
if (count($row) == 1) {
return true;
}
else {
return false;
}
}
When someone logs in, I have their UID. I want to see if that's in the DB already. It's basic logic will be used in a
"If exists, display preferences, if !exists, display signup box" sort of flow. Obviously it's dependent on how it's used in the rest of the code, but will this work as advertised and have I fallen for any pitfalls? Thanks!
Try this:
$conn = get_db_conn(); # should reuse a connection if it exists
# Have MySQL count the rows, instead of fetching a list (also prevent injection)
$res = mysql_query(sprintf("SELECT COUNT(*) FROM users WHERE uid=%d", $user));
# if the query fails
if (!$res) return false;
# explode the result
list($count) = mysql_fetch_row($res);
return ($count === '1');
Thoughts:
You'll want better handling of a failed query, since return false means the user doesn't already exist.
Use the database to count, it'll be faster.
I'm assuming uid is an integer in the sprintf statement. This is now safe for user input.
If you have an if statement that looks like if (something) { true } else { false } you should collapse it to just return something.
HTH
That is reuseable, yes. You may want to consider moving the SQL out of the PHP code itself.
Although you weren't asking for optimization necessarily, you might want to consider querying for the user's display preferences (which I assume are stored in the DB) and if it comes back empty, display the signup box. You'll save a trip to the database and depending on your traffic, that could be huge. If you decide to keep this implementation, I would suggest only selecting one column from the database in your SELECT. As long as you don't care about the data, there's no reason to fetch every single column.
First off, you need to call
$user = mysql_real_escape_string($user);
because there's an sql injection bug in your code, see the manual. Second, you can simplify your logic by changing your query to:
SELECT COUNT(1) FROM user WHERE uid = $user;
which just lets you evaluate a single return value from $row. Last thing, once you have the basics of php down, consider looking at a php framework. They can cause you trouble and won't make you write good code, but they likely will save you a lot of work.
Indent!
Overall it looks not bad...check the comments..
function test_user($user)
{
$conn = get_db_conn(); //this should be done only once. Maybe somewhere else...?
$res = mysql_query("SELECT uid FROM users WHERE uid = $user");
$row = mysql_fetch_assoc($res);
//I can't remember...can you return count($row) and have that forced to boolean ala C? It would reduce lines of code and make it easier to read.
if (count($row) == 1) {
return true;
}
else {
return false;
}
}
Also,
if (condition) {
return true;
}
else {
return false;
}
can be rewritten as:
return condition;
which saves quite a bit of typing and reading :)

Categories