Hello i did use the search before posting this.
Im new to php/mysql been doing soooo much reading. have been able to make a game that a few friends are playing. its like a pvp game.
Anyway one of the people playing found a way to glitch buying and selling units by putting a . in front of the value. i do have a protect feature for stripping illegal characters
function protect($string) {
return mysql_real_escape_string(strip_tags(addslashes($string)));
}
this works for other characters but not with . im not asking for someone to do it for me just wanted to be pointed in the right direction.
but just encase someone asks here is the code im using
if(isset($_POST['buy'])){
$sword = protect($_POST['sword']);
$shield = protect($_POST['shield']);
$gold_needed = (10 * $sword) + (10 * $shield);
if($sword < 0 || $shield < 0){
output("You must buy a positive number of weapons!");
}elseif($stats['gold'] < $gold_needed){
output("You do not have enough gold!");
}else{
$weapon['sword'] += $sword;
$weapon['shield'] += $shield;
$update_weapons = mysql_query("UPDATE `weapon` SET
`sword`='".$weapon['sword']."',
`shield`='".$weapon['shield']."'
WHERE `id`='".$_SESSION['uid']."'") or die(mysql_error());
$stats['gold'] -= $gold_needed;
$update_gold = mysql_query("UPDATE `stats` SET `gold`='".$stats['gold']."'
WHERE `id`='".$_SESSION['uid']."'") or die(mysql_error());
include("update_stats.php");
output("You have bought weapons!");
}
If anyone could give me a hand i would greatly appreciate it
i did find something "string functions, substr replace and str replace"
but can i use two functions in 1 query? sorry im new
EDIT***
Here is the query posted in update_stats
$update_stats = mysql_query("UPDATE `stats` SET
`income`='".$income."',`farming`='".$farming."',
`attack`='".$attack."',`defense`='".$defense."'
WHERE `id`='".$_SESSION['uid']."'") or die(mysql_error());
one of the people playing found a way to glitch buying and selling units by putting a . in front of the value
Well, you've not disclosed EXACTLY what the vulnerability is, but I'll hazard a guess that by input of a decimal value they run around your pricing/math? So, a number of possibilities, I should think?
if (substr($string, 0, 1) == ".") {
//return false, warn, etc.
}
That could go in your "protect" function.
Likewise, you could use intval() or even is_numeric() ... here I just add it to the assignment:
$sword = protect(intval($_POST['sword']));
You could also play with a regular expression. I'm assuming $value to be numeric? How many digits max? I've used 5:
if (preg_match("%\.\d{1,5}%", $sword)) { //this guy's playing w/us
die("Go away, bad hax0rz! :-P");
}
Related
I am currently dealing with a performance issue where I cannot find a way to fix it. I want to search a text for usernames mentioned with the # sign in front. The list of usernames is available as PHP array.
The problem is usernames may contain spaces or other special characters. There is no limitation for it. So I can't find a regex dealing with that.
Currently I am using a function which gets the whole line after the # and checks char by char which usernames could match for this mention, until there is just one username left which totally matches the mention. But for a long text with 5 mentions it takes several seconds (!!!) to finish. for more than 20 mentions the script runs endlessly.
I have some ideas, but I don't know if they may work.
Going through username list (could be >1.000 names or more) and search for all #Username without regex, just string search. I would say this would be far more inefficient.
Checking on writing the usernames with JavaScript if space or punctual sign is inside the username and then surround it with quotation marks. Like #"User Name". Don't like that idea, that looks dirty for the user.
Don't start with one character, but maybe 4. and if no match, go back. So same principle like on sorting algorithms. Divide and Conquer. Could be difficult to implement and will maybe lead to nothing.
How does Facebook or twitter and any other site do this? Are they parsing the text directly while typing and saving the mentioned usernames directly in the stored text of the message?
This is my current function:
$regular_expression_match = '#(?:^|\\s)#(.+?)(?:\n|$)#';
$matches = false;
$offset = 0;
while (preg_match($regular_expression_match, $post_text, $matches, PREG_OFFSET_CAPTURE, $offset))
{
$line = $matches[1][0];
$search_string = substr($line, 0, 1);
$filtered_usernames = array_keys($user_list);
$matched_username = false;
// Loop, make the search string one by one char longer and see if we have still usernames matching
while (count($filtered_usernames) > 1)
{
$filtered_usernames = array_filter($filtered_usernames, function ($username_clean) use ($search_string, &$matched_username) {
$search_string = utf8_clean_string($search_string);
if (strlen($username_clean) == strlen($search_string))
{
if ($username_clean == $search_string)
{
$matched_username = $username_clean;
}
return false;
}
return (substr($username_clean, 0, strlen($search_string)) == $search_string);
});
if ($search_string == $line)
{
// We have reached the end of the line, so stop
break;
}
$search_string = substr($line, 0, strlen($search_string) + 1);
}
// If there is still one in filter, we check if it is matching
$first_username = reset($filtered_usernames);
if (count($filtered_usernames) == 1 && utf8_clean_string(substr($line, 0, strlen($first_username))) == $first_username)
{
$matched_username = $first_username;
}
// We can assume that $matched_username is the longest matching username we have found due to iteration with growing search_string
// So we use it now as the only match (Even if there are maybe shorter usernames matching too. But this is nothing we can solve here,
// This needs to be handled by the user, honestly. There is a autocomplete popup which tells the other, longer fitting name if the user is still typing,
// and if he continues to enter the full name, I think it is okay to choose the longer name as the chosen one.)
if ($matched_username)
{
$startpos = $matches[1][1];
// We need to get the endpos, cause the username is cleaned and the real string might be longer
$full_username = substr($post_text, $startpos, strlen($matched_username));
while (utf8_clean_string($full_username) != $matched_username)
{
$full_username = substr($post_text, $startpos, strlen($full_username) + 1);
}
$length = strlen($full_username);
$user_data = $user_list[$matched_username];
$mentioned[] = array_merge($user_data, array(
'type' => self::MENTION_AT,
'start' => $startpos,
'length' => $length,
));
}
$offset = $matches[0][1] + strlen($search_string);
}
Which way would you go? The problem is the text will be displayed often and parsing it every time will consume a lot of time, but I don't want to heavily modify what the user had entered as text.
I can't find out what's the best way, and even why my function is so time consuming.
A sample text would be:
Okay, #Firstname Lastname, I mention you!
Listen #[TEAM] John, you are a team member.
#Test is a normal name, but #Thât♥ should be tracked too.
And see #Wolfs garden! I just mean the Wolf.
Usernames in that text would be
Firstname Lastname
[TEAM] John
Test
Thât♥
Wolf
So yes, there is clearly nothing I know where a name may end. Only thing is the newline.
I think the main problem is, that you can't distinguish usernames from text and it's a bad idea, to lookup maybe thousands of usernames in a text, also this can lead to further problems, that John is part of [TEAM] John or JohnFoo...
It's needed to separate the usernames from other text. Assuming that you're using UTF-8, could put the usernames inside invisible zero-w space \xE2\x80\x8B and non-joiner \xE2\x80\x8C.
The usernames can now be extracted fast and with little effort and if needed still verified in db.
$txt = "
Okay, #\xE2\x80\x8BFirstname Lastname\xE2\x80\x8C, I mention you!
Listen #\xE2\x80\x8B[TEAM] John\xE2\x80\x8C, you are a team member.
#\xE2\x80\x8BTest\xE2\x80\x8C is a normal name, but
#\xE2\x80\x8BThât?\xE2\x80\x8C should be tracked too.
And see #\xE2\x80\x8BWolfs\xE2\x80\x8C garden! I just mean the Wolf.";
// extract usernames
if(preg_match_all('~#\xE2\x80\x8B\K.*?(?=\xE2\x80\x8C)~s', $txt, $out)){
print_r($out[0]);
}
Array
(
[0] => Firstname Lastname
1 => [TEAM] John
2 => Test
3 => Thât♥
4 => Wolfs
)
echo $txt;
Okay, #Firstname Lastname, I mention you!
Listen #[TEAM] John, you are a team member.
#Test is a normal name, but
#Thât♥ should be tracked too.
And see #Wolfs garden! I just mean the Wolf.
Could use any characters you like and that possibly don't occur elsewhere for separation.
Regex FAQ, Test at eval.in (link will expire soon)
so im adding commands to a very popular vbulletin plugin, and there's this one i cant seem to get to work properly no matter what i try. The code i have so far is
$cybcb_checkban = strpos($vbulletin->GPC['ccb_newmessage'], '/ban');
if($cybcb_checkban !== false AND $cybcb_candelall)
{
$banuserurl = $vbulletin->options['bburl'].'/misc.php?'.$vbulletin->session->vars['sessionurl'].'do=ccb_banuser&u=2';exec_header_redirect($banuserurl);
}
what i need it to do is the part that says
do=ccb_banuser&u=2
is replace the 2 with the text entered in my chat.
vbulletin kinda has its own language but the base concept of it is PHP
but for reference, this is a default command already in the plugin
$cybcb_checkme = strpos($vbulletin->GPC['ccb_newmessage'], '/me ');
if ($cybcb_checkme !== false)
{
$vbulletin->GPC['ccb_newmessage'] = str_replace('/me ', $vbulletin->userinfo['username'].' ', $vbulletin->GPC['ccb_newmessage']);
$vbulletin->GPC['ccb_newmessage'] = '[color='.$vbulletin->options['cybchatbox_mecolor'].']* '.$vbulletin->GPC['ccb_newmessage'].'[/color]';
}
I've been playing around with this for a few days now, and the only result I've got semi working is the first code at the top, the problem with that as you may have guess by entering
/ban (USER ID HERE)
the only user that will get banned is user 2 (obviously)
so i just need something that will give the code the ability to be /ban (userID)
i know its got to be something like
$banuserurl = $vbulletin->options['bburl'].'/misc.php?'.$vbulletin->session->vars['sessionurl'].'do=ccb_banuser&u=.$vbulletin->GPC['ccb_newmessage']';exec_header_redirect($banuserurl);
but that doesnt seem to work =/ anyone have the answer? or can point me in the right direction, I'd be very grateful =) thanks.
$vbulletin->GPC['ccb_newmessage'] will contain the whole message, so if you send
/ban 123455
$vbulletin->GPC['ccb_newmessage'] will have /ban 123455
what you can then do is remove '/ban ' and you'll be left with the User ID
so something like
$cybcb_checkban = strpos($vbulletin->GPC['ccb_newmessage'], '/ban');
if($cybcb_checkban !== false AND $cybcb_candelall)
{
$vbulletin->GPC['ccb_newmessage'] = str_replace('/ban ', '', $vbulletin->GPC['ccb_newmessage']);
$banuserurl = $vbulletin->options['bburl'].'/misc.php?'.$vbulletin->session->vars['sessionurl'].'do=ccb_banuser&u=' . $vbulletin->GPC['ccb_newmessage'];
exec_header_redirect($banuserurl);
}
or
$cybcb_checkban = strpos($vbulletin->GPC['ccb_newmessage'], '/ban');
if($cybcb_checkban !== false AND $cybcb_candelall)
{
$banuserurl = $vbulletin->options['bburl'].'/misc.php?'.$vbulletin->session->vars['sessionurl'].'do=ccb_banuser&u=' . substr($vbulletin->GPC['ccb_newmessage'], 4);
exec_header_redirect($banuserurl);
}
should work
note: that 4 should probably be replaced with something like substr($vbulletin->GPC['ccb_newmessage'], strpos($vbulletin->GPC['ccb_newmessage'], '/ban') + strlen('/ban'));
I've had excellent support here, so I figured I'd try again, as I have no clue where to even begin looking for this answer.
I have a simple MySQL database, named "testimonials", which contains 3 tables, "id", "name", "content"
What I want to do, is display testimonials within a fixed size block. Just simply displaying the content is no problem at all, however where I'm stuck is the (somewhat) unique way I'm trying to make it work. I would like to display a random item on each page load, and then to check the character length of the "content" within the testimonial, and if it's equal to or greater than XX length, then just display the one testimonial, otherwise if it's less than XX in length to display a second testimonial (assuming it combined with the first doesn't break the container box).
The box in question is 362px in width and 353px in height using 14px font with Verdana. An example of how the testimonial will appear on the page is like this:
"This is the testimonial content, some nice message from a client."
-- Billy Bob, Owner, Crazy Joe's Tavern
The "name" table in the database holds everything in bold (minus the -- of course), in case someone felt the need to ask.
As I typed that, I felt as if I was asking for a miracle, however I'll still post the question, hoping someone might just know the answer. As always, thanks for any help I may get, if this is just simply asking too much, I'm not totally against the idea of only displaying one testimonial at a time making a ground rule saying they have to contain a minimum of XX characters.
Thanks!
Quick Update: I didn't expect to get answers so quickly, I'm not at my desk at the moment, so as soon as I sit back down I'll go through and see which answer fits best. However, do you guys get together and try to make your answer more complex than the previous answer? lol, thanks though, for anyone who's offering help, you guys rock!
Final edit: I decided against this whole idea, as it just way over complicated everything. For the time being, I'm just going to display all testimonials, and make them scroll, while I work on a jQuery snippet to make it prettier. Thanks everyone for your help though! Should I decide again to do this, I'll be trying my chosen answer.
You just need a loop. Pseudo-code:
$length = 0;
$target = 200; // or whatever
while( $length < $target ) {
$comment = getOneComment();
displayComment($comment);
$length += strlen( $comment['content'] ); // assuming getOneComment() returns an associative array
}
To make it pretty, if the display box is going to a be a fixed height, you could use some jQuery to toggle whether to show the second comment on not.
Assuming you have testimonials in an array:
$testimonials = array(
't1' => array(
'content' => 'testimonials 1 content..',
'author' => 'the author'
),
't2' => array(
'content' => 'testimonials 2 content..',
'author' => 'the author 2'
),
);
You could have an maxLengthTestimonialsContent and maxLenthAllTestimonnials variable :
$maxLengthTestimonialsContent = 120;
$maxLenthAllTestimonnials = 240;
And now with a simple loop you build the array testimonials that you will use to show:
$testimonialsToShow = array();
$i = 1; $totalLength = 0
foreach($testimonials as $t) {
if( $i > 1 && strlen( $t['content']) < $maxLengthTestimonialsContent
&& $totalLength < $maxLenthAllTestimonnials )
break; // basically here you test that testimonials less first
// and with less length than maxLengthTestimonial, and also
// total length less than maxLengthAll to be stored
//in $testimonialsToShow
else {
$testimonialsToShow[] = $t;
$totalLength = $t['content'];
}
}
Something like this is what you would need.
<?php
$str = $res["testimonial"];
if (strlen($str) > 50) {
// Logic to retrieve and display second testimonial
}
?>
Obviously there's some more processing you'll have to come up with to determine if the second testimonial is short enough to fit or not. But that should get you started.
EDIT:
For the randomization, I use this on my own site:
$referrals = mysql_query("SELECT id FROM ts_testimonials");
$referralView = array();
$i = 0;
while ($newReferral = mysql_fetch_array($referrals)) {
$referralView[$i] = $newReferral['id'];
$i++;
}
if (sizeof($referralView) >= 1){
$referralTop = rand(0,sizeof($referralView)-1);
$newReferralTop = mysql_fetch_array(mysql_query("SELECT * FROM ts_testimonials WHERE id = '".$referralView[$referralTop]."'"));
if (sizeof($referralView) >=2){
$referralBottom = rand(0,sizeof($referralView)-1);
while ($referralBottom == $referralTop) {
$referralBottom = rand(0,sizeof($referralView)-1);
}
$newReferralBottom = mysql_fetch_array(mysql_query("SELECT * FROM ts_testimonials WHERE id = '".$referralView[$referralBottom]."'"));
}
}
say if I wanted to give every user that registered on my site a unique id. It seems to me that if I wanted to do this I would have to: Create a random number for the id, check to see if that id already exists in the database, if it does exist then create another random number and send yet another query to see if that exists, and so on...
This could go on for ages. Apart from having an incrementing id, is there any decent way to do this?
The best way to do this is via the auto increment function, if you really don't want to use a function like so you could use uniqid();
Basically you it generates an unique id based on milliseconds, if you put in a kinda unique prefix in the function it will generate a very unique id.
echo uniqid('prefix');
This way you won't have to check after generating an id, if it already exists or not. You can be sure it is unique.
For more information check this url http://php.net/uniqid!
First of all, I agree with the comments. It's all overhead code, and if you're using it to make it look interesting you should really reconsider your priorities.
But, if you still need it; here's a little something:
function uid() {
mt_srand((double)microtime()*1000000);
$token = mt_rand(1, mt_getrandmax());
$uid = uniqid(md5($token), true);
if($uid != false && $uid != '' && $uid != NULL) {
$out = sha1($uid);
return $out;
} else {
return false;
}
}
Basically, it does a lot of random number generating to create a token for uniqueid, and then is sha's that. Probably overhead, but you can be sure that you never generate a double uid.
Fabian.
You can use the rand() function. It will generate a random number between two.
rand(0000,9999)
It will generate a number between 0 and 9999.
To check if it already exist:
$id = rand(0000,9999);
/* CREATE YOUR MYSQL CONNECTION */
$user_list = mysql_query("SELECT * FROM users");
while ($user = mysql_fetch_array($user_list))
{
if ($id == $user['id'])
{
echo('Already exist.');
}
else
{
/* YOUR CODE */
}
}
It's the way I did it...
If you have a string of 15 numbers you are looking at up to 999 trillion, I doubt it will run for "ages" considering there's almost 7 billion people on the planet.
Does the ID need to be numeric? By switching to alphabetic characters you will get a lot more entropy. A 6 digit number is 1,000,000 posibilities, a 6 character alphanumeric string is 2,176,782,336 possibilities. Make it mixed case alphanumeric and that jumps to 15,625,000,000.
Here's how I usually generate unique strings that are as short as possible:
$chars = 'abcdefghijklmnopqrstuvwrxyzABCDEFGHIJKLMNOPQRSTUVWRXYZ0123456789';
mt_srand((double)microtime()*1000000);
$id = '';
do {
$id .= $chars[mt_rand(0, strlen($chars) - 1)];
} while (isIdTaken($id));
var_dump($id);
You have to create a lot of items with this style of id, before you'll get to more than 3 or 4 characters.
I know it's late for this answer but the easiest solution is to generate random number and sure it will be unique 100% is
$uid = uniqid().date("Ymdhhis");
I'd like to be able to use php search an array (or better yet, a column of a mysql table) for a particular string. However, my goal is for it to return the string it finds and the number of matching characters (in the right order) or some other way to see how reasonable the search results are, so then I can make use of that info to decide if I want to display the top result by default or give the user options of the top few.
I know I can do something like
$citysearch = mysql_query(" SELECT city FROM $table WHERE city LIKE '$city' ");
but I can't figure out a way to determine how accurate it is.
The goal would be:
a) find "Milwaukee" if the search term were "milwakee" or something similar.
b) if the search term were "west", return things like "West Bend" and "Westmont".
Anyone know a good way to do this?
You should check out full text searching in MySQL. Also check out Zend's port of the Apache Lucene project, Zend_Search_Lucene.
More searching led me to the Levenshtein distance and then to similar_text, which proved to be the best way to do this.
similar_text("input string", "match against this", $pct_accuracy);
compares the strings and then saves the accuracy as a variable. The Levenshtein distance determines how many delete, insert, or replace functions on a single character it would need to do to get from one string to the other, with an allowance for weighting each function differently (eg. you can make it cost more to replace a character than to delete a character). It's apparently faster but less accurate than similar_text. Other posts I've read elsewhere have mentioned that for strings of fewer than 10000 characters, there's no functional difference in speed.
I ended up using a modified version of something I found to make it work. This ends up saving the top 3 results (except in the case of an exact match).
$input = $_POST["searchcity"];
$accuracy = 0;
$runner1acc = 0;
$runner2acc = 0;
while ($cityarr = mysql_fetch_row($allcities)) {
$cityname = $cityarr[1];
$cityid = $cityarr[0];
$city = strtolower($cityname);
$diff = similar_text($input, $city, $tempacc);
// check for an exact match
if ($tempacc == '100') {
// closest word is this one (exact match)
$closest = $cityname;
$closestid = $cityid;
$accuracy = 100;
break;
}
if ($tempacc >= $accuracy) { // more accurate than current leader
$runner2 = $runner1;
$runner2id = $runner1id;
$runner2acc = $runner1acc;
$runner1 = $closest;
$runner1id = $closestid;
$runner1acc = $accuracy;
$closest = $cityname;
$closestid = $cityid;
$accuracy = $tempacc;
}
if (($tempacc < $accuracy)&&($tempacc >= $runner1acc)) { // new 2nd place
$runner2 = $runner1;
$runner2id = $runner1id;
$runner2acc = $runner1acc;
$runner1 = $cityname;
$runner1id = $cityid;
$runner1acc = $tempacc;
}
if (($tempacc < $runner1acc)&&($tempacc >= $runner2acc)) { // new 3rd place
$runner2 = $cityname;
$runner2id = $cityid;
$runner2acc = $tempacc;
}
}
echo "Input word: $input\n<BR>";
if ($accuracy == 100) {
echo "Exact match found: $closestid $closest\n";
} elseif ($accuracy > 70) { // for high accuracies, assumes that it's correct
echo "We think you meant $closestid $closest ($accuracy)\n";
} else {
echo "Did you mean:<BR>";
echo "$closestid $closest? ($accuracy)<BR>\n";
echo "$runner1id $runner1 ($runner1acc)<BR>\n";
echo "$runner2id $runner2 ($runner2acc)<BR>\n";
}
This can be very complicated, and I am not personally aware of any good 3rd party libraries although I'm sure they exist. Others may be able to suggest some canned solutions, though.
I have written something similar from scratch a few times in the past. If you go down that route, it is probably not something you'd want to do in PHP by itself as every query would involve getting all of the records and performing your calculations on them. It will almost certainly involve creating a set of index tables that meet your specifications.
For instance, you would have to come up with rules for how you imagine that "Milwaukee" could end up spelled "milwakee." My solution to this was to do vowel compression and duplication compression (not sure if these are actually search terms). So, milwaukee would be indexed as:
milwaukee
m_lw__k__
m_lw_k_
When the search query came in for "milwaukee", I would run the same process on the text input, and then run a search on the index table for:
SELECT cityId,
COUNT(*)
FROM myCityIndexTable
WHERE term IN ('milwaukee', 'm_lw__k__', 'm_lw_k_')
When the search query came in for "milwakee", I would run the same process on the text input, and then run a search on the index table for:
SELECT cityId,
COUNT(*)
FROM myCityIndexTable
WHERE term IN ('milwaukee', 'm_lw_k__', 'm_lw_k_')
In the case of Milwaukee (spelled correctly), it would return "3" for the count.
In the case of Milwakee (spelled incorrectly) ,it would return "2" for the count (since it would not match the m_lw__k__ pattern as it only had one vowel in the middle).
If you sort the results based on the count, you would end up meeting one of your rules, that "Milwaukee" would end up being sorted higher as a possible match than "Milwakee."
If you want to build this system in a generic way (as hinted by your use of $table in the query) then you'd probably need another mapping table somewhere in there to map your terms to the appropriate table.
I'm not suggesting this is the best (or even a good) way to go about this, just something I've done in the past that might prove useful to you if you plan to try and do this without a third party solution.
Most maddening result with LIKE is this one "%man" this will return all woman in file!
In case of listing perhaps a not too bad solution is to keep on shortening the searching needle. In your case a match will come up when your searching $ is as short as "milwa".